<commit_before>package poker

import (
	"fmt"
	"log"
	"reflect"
	"sort"
)

type Hand struct {
	rawHand string
	cards   []Card
}

func NewHand(rawHand string) (hand Hand, err error) {
	cards, err := parseCards(rawHand)
	if err != nil {
		return Hand{}, err
	}
	return Hand{
		rawHand: rawHand,
		cards:   cards,
	}, nil

}

func parseHands(rawHands []string) (hands []Hand, err error) {
	for _, rawHand := range rawHands {
		hand, err := NewHand(rawHand)
		if err != nil {
			return []Hand{}, err
		}
		hands = append(hands, hand)
	}
	return hands, nil
}

func (h Hand) Ranks() (ranks []Rank) {
	for _, card := range h.cards {
		ranks = append(ranks, card.rank)
	}
	return ranks
}

// compare returns 0 if both hands have equal score
// compare returns a positive number if the score of h is less than the score of o
// compare returns a negative number if the score of h is greater than the score of o
func (h Hand) compare(o Hand) (int, error) {
	if h.handType() == o.handType() {
		return h.tiebreak(o)
	}
	return int(h.handType()) - int(o.handType()), nil
}

// compare returns 0 if both hands have equal score
// compare returns a positive number if the score of h is less than the score of o
// compare returns a negative number if the score of h is greater than the score of o
func (h Hand) tiebreak(o Hand) (int, error) {
	if h.handType() != o.handType() {
		log.Fatalf("tiebreak invoked with unequal hand types %v and %v", h, o)
	}

	switch h.handType() {
	case FourOfKind:
		comparison, err := h.compareByHighestQuad(o)
		if err != nil {
			return 0, err
		}
		if comparison != 0 {
			return comparison, nil
		}
		return 0, nil
	case FullHouse:
		comparison, err := h.compareByHighestTriplet(o)
		if err != nil {
			return 0, err
		}
		if comparison != 0 {
			return comparison, nil
		}

		comparison, err = h.compareByHighestPair(o)
		if err != nil {
			return 0, err
		}
		if comparison != 0 {
			return comparison, nil
		}

		return 0, nil
	case Straight:
		if h.isStraightStartingWithAce() && o.isStraightStartingWithAce() {
			return 0, nil
		} else if h.isStraightStartingWithAce() {
			return -1, nil
		} else if o.isStraightStartingWithAce() {
			return 1, nil
		}
		return h.compareByHighestCard(o), nil
	default:
		return h.compareByHighestCard(o), nil
	}
}

func (h Hand) compareByHighestCard(o Hand) int {
	descendingRanksH := h.descendingRanks()
	descendingRanksO := o.descendingRanks()

	for i := range descendingRanksH {
		rankH := descendingRanksH[i]
		rankO := descendingRanksO[i]
		if rankH != rankO {
			return int(rankH) - int(rankO)
		}
	}
	return 0
}

func (h Hand) compareByHighestQuad(o Hand) (int, error) {
	quadH, err := h.quadRank()
	if err != nil {
		return 0, err
	}

	quadO, err := o.quadRank()
	if err != nil {
		return 0, err
	}

	return int(quadH) - int(quadO), nil
}

func (h Hand) compareByHighestTriplet(o Hand) (int, error) {
	tripletH, err := h.tripletRank()
	if err != nil {
		return 0, err
	}

	tripletO, err := o.tripletRank()
	if err != nil {
		return 0, err
	}

	return int(tripletH) - int(tripletO), nil
}

func (h Hand) compareByHighestPair(o Hand) (int, error) {
	pairH, err := h.pairRank()
	if err != nil {
		return 0, err
	}

	pairO, err := o.pairRank()
	if err != nil {
		return 0, err
	}

	return int(pairH) - int(pairO), nil
}

func (h Hand) quadRank() (rank Rank, err error) {
	rankToOccurences := h.getRankToOccurences()
	for rank, occurences := range rankToOccurences {
		if occurences == 4 {
			return rank, nil
		}
	}
	return rank, fmt.Errorf("hand %v does not have a quad", h)
}

func (h Hand) tripletRank() (rank Rank, err error) {
	rankToOccurences := h.getRankToOccurences()
	for rank, occurences := range rankToOccurences {
		if occurences == 3 {
			return rank, nil
		}
	}
	return rank, fmt.Errorf("hand %v does not have a triplet", h)
}

func (h Hand) pairRank() (rank Rank, err error) {
	rankToOccurences := h.getRankToOccurences()
	for rank, occurences := range rankToOccurences {
		if occurences == 2 {
			return rank, nil
		}
	}
	return rank, fmt.Errorf("hand %v does not have a pair", h)
}

func (h Hand) descendingRanks() (ranks []Rank) {
	for _, card := range h.cards {
		ranks = append(ranks, card.rank)
	}

	sort.Sort(sort.Reverse(ByRank(ranks)))
	return ranks
}

func (h Hand) handType() HandType {
	switch {
	case h.isStraightFlush():
		return StraightFlush
	case h.isFourOfKind():
		return FourOfKind
	case h.isFullHouse():
		return FullHouse
	case h.isFlush():
		return Flush
	case h.isStraight():
		return Straight
	case h.isThreeOfKind():
		return ThreeOfKind
	case h.isTwoPairs():
		return TwoPairs
	case h.isTwoOfKind():
		return Pair
	default:
		return Nothing
	}
}

func (h Hand) String() string {
	return fmt.Sprintf("handType %v with cards %v", h.handType(), h.cards)
}

func (h Hand) isStraightFlush() bool {
	return h.isStraight() && h.isFlush()
}

func (h Hand) isFourOfKind() bool {
	rankToOccurences := h.getRankToOccurences()
	for _, occurences := range rankToOccurences {
		if occurences == 4 {
			return true
		}
	}
	return false
}

func (h Hand) isFullHouse() bool {
	rankToOccurences := h.getRankToOccurences()
	occurences := getValues(rankToOccurences)
	sort.Ints(occurences)
	fullHouse := []int{2, 3}
	return reflect.DeepEqual(occurences, fullHouse)
}

func (h Hand) isFlush() bool {
	suitToOccurences := h.getSuitToOccurences()
	for _, occurences := range suitToOccurences {
		if occurences != 5 {
			return false
		}
	}
	return true
}

func (h Hand) isStraight() bool {
	if h.isStraightStartingWithAce() {
		return true
	}

	ranks := h.Ranks()
	sort.Sort(ByRank(ranks))
	for i, rank := range ranks {
		if i == 0 {
			continue
		}
		if int(ranks[i-1]) != int(rank)-1 {
			return false
		}
	}
	return true
}

func (h Hand) isStraightStartingWithAce() bool {
	ranks := h.Ranks()
	sort.Sort(ByRank(ranks))
	straightStartingWithAce := []Rank{Two, Three, Four, Five, Ace}
	return reflect.DeepEqual(ranks, straightStartingWithAce)
}

func (h Hand) isThreeOfKind() bool {
	rankToOccurences := h.getRankToOccurences()
	for _, occurences := range rankToOccurences {
		if occurences == 3 {
			return true
		}
	}
	return false
}

func (h Hand) isTwoPairs() bool {
	rankToOccurences := h.getRankToOccurences()
	occurences := getValues(rankToOccurences)
	sort.Ints(occurences)
	twoPairs := []int{1, 2, 2}
	return reflect.DeepEqual(occurences, twoPairs)
}

func (h Hand) isTwoOfKind() bool {
	rankToOccurences := h.getRankToOccurences()
	for _, occurences := range rankToOccurences {
		if occurences == 2 {
			return true
		}
	}
	return false
}

func (h Hand) getRankToOccurences() (occurences map[Rank]int) {
	occurences = map[Rank]int{}
	for _, card := range h.cards {
		if _, ok := occurences[card.rank]; !ok {
			occurences[card.rank] = 0
		}
		occurences[card.rank] += 1
	}
	return occurences
}

func (h Hand) getSuitToOccurences() (occurences map[Suit]int) {
	occurences = map[Suit]int{}
	for _, card := range h.cards {
		if _, ok := occurences[card.suit]; !ok {
			occurences[card.suit] = 0
		}
		occurences[card.suit] += 1
	}
	return occurences
}

type ByScore []Hand

func (s ByScore) Len() int          { return len(s) }
func (s ByScore) Swap(a int, b int) { s[a], s[b] = s[b], s[a] }
func (s ByScore) Less(a int, b int) bool {
	handA := s[a]
	handB := s[b]
	comparison, err := handA.compare(handB)
	if err != nil {
		log.Fatal(err)
	}
	return comparison < 0
}

func getValues(rankToOccurences map[Rank]int) (values []int) {
	for _, v := range rankToOccurences {
		values = append(values, v)
	}
	return values
}
<commit_msg>Solve poker<commit_after>package poker

import (
	"fmt"
	"log"
	"reflect"
	"sort"
)

type Hand struct {
	rawHand string
	cards   []Card
}

func NewHand(rawHand string) (hand Hand, err error) {
	cards, err := parseCards(rawHand)
	if err != nil {
		return Hand{}, err
	}
	return Hand{
		rawHand: rawHand,
		cards:   cards,
	}, nil

}

func parseHands(rawHands []string) (hands []Hand, err error) {
	for _, rawHand := range rawHands {
		hand, err := NewHand(rawHand)
		if err != nil {
			return []Hand{}, err
		}
		hands = append(hands, hand)
	}
	return hands, nil
}

func (h Hand) Ranks() (ranks []Rank) {
	for _, card := range h.cards {
		ranks = append(ranks, card.rank)
	}
	return ranks
}

// compare returns 0 if both hands have equal score
// compare returns a positive number if the score of h is less than the score of o
// compare returns a negative number if the score of h is greater than the score of o
func (h Hand) compare(o Hand) (int, error) {
	if h.handType() == o.handType() {
		return h.tiebreak(o)
	}
	return int(h.handType()) - int(o.handType()), nil
}

// compare returns 0 if both hands have equal score
// compare returns a positive number if the score of h is less than the score of o
// compare returns a negative number if the score of h is greater than the score of o
func (h Hand) tiebreak(o Hand) (int, error) {
	if h.handType() != o.handType() {
		log.Fatalf("tiebreak invoked with unequal hand types %v and %v", h, o)
	}

	switch h.handType() {
	case FourOfKind:
		comparison, err := h.compareByHighestQuad(o)
		if err != nil {
			return 0, err
		}
		if comparison != 0 {
			return comparison, nil
		}

		comparison, err = h.compareByHighestKicker(o)
		if err != nil {
			return 0, err
		}
		if comparison != 0 {
			return comparison, nil
		}
		return 0, nil
	case FullHouse:
		comparison, err := h.compareByHighestTriplet(o)
		if err != nil {
			return 0, err
		}
		if comparison != 0 {
			return comparison, nil
		}

		comparison, err = h.compareByHighestPair(o)
		if err != nil {
			return 0, err
		}
		if comparison != 0 {
			return comparison, nil
		}

		return 0, nil
	case Straight:
		if h.isStraightStartingWithAce() && o.isStraightStartingWithAce() {
			return 0, nil
		} else if h.isStraightStartingWithAce() {
			return -1, nil
		} else if o.isStraightStartingWithAce() {
			return 1, nil
		}
		return h.compareByHighestCard(o), nil
	default:
		return h.compareByHighestCard(o), nil
	}
}

func (h Hand) compareByHighestCard(o Hand) int {
	descendingRanksH := h.descendingRanks()
	descendingRanksO := o.descendingRanks()

	for i := range descendingRanksH {
		rankH := descendingRanksH[i]
		rankO := descendingRanksO[i]
		if rankH != rankO {
			return int(rankH) - int(rankO)
		}
	}
	return 0
}

func (h Hand) compareByHighestQuad(o Hand) (int, error) {
	quadH, err := h.quadRank()
	if err != nil {
		return 0, err
	}

	quadO, err := o.quadRank()
	if err != nil {
		return 0, err
	}

	return int(quadH) - int(quadO), nil
}

func (h Hand) compareByHighestKicker(o Hand) (int, error) {
	kickerH, err := h.kicker()
	if err != nil {
		return 0, err
	}

	kickerO, err := o.kicker()
	if err != nil {
		return 0, err
	}

	return int(kickerH) - int(kickerO), nil
}

func (h Hand) compareByHighestTriplet(o Hand) (int, error) {
	tripletH, err := h.tripletRank()
	if err != nil {
		return 0, err
	}

	tripletO, err := o.tripletRank()
	if err != nil {
		return 0, err
	}

	return int(tripletH) - int(tripletO), nil
}

func (h Hand) compareByHighestPair(o Hand) (int, error) {
	pairH, err := h.pairRank()
	if err != nil {
		return 0, err
	}

	pairO, err := o.pairRank()
	if err != nil {
		return 0, err
	}

	return int(pairH) - int(pairO), nil
}

func (h Hand) quadRank() (rank Rank, err error) {
	rankToOccurences := h.getRankToOccurences()
	for rank, occurences := range rankToOccurences {
		if occurences == 4 {
			return rank, nil
		}
	}
	return rank, fmt.Errorf("hand %v does not have a quad", h)
}

func (h Hand) tripletRank() (rank Rank, err error) {
	rankToOccurences := h.getRankToOccurences()
	for rank, occurences := range rankToOccurences {
		if occurences == 3 {
			return rank, nil
		}
	}
	return rank, fmt.Errorf("hand %v does not have a triplet", h)
}

func (h Hand) pairRank() (rank Rank, err error) {
	rankToOccurences := h.getRankToOccurences()
	for rank, occurences := range rankToOccurences {
		if occurences == 2 {
			return rank, nil
		}
	}
	return rank, fmt.Errorf("hand %v does not have a pair", h)
}

func (h Hand) kicker() (rank Rank, err error) {
	rankToOccurences := h.getRankToOccurences()
	for rank, occurences := range rankToOccurences {
		if occurences == 1 {
			return rank, nil
		}
	}
	return rank, fmt.Errorf("hand %v does not have a kicker", h)
}

func (h Hand) descendingRanks() (ranks []Rank) {
	for _, card := range h.cards {
		ranks = append(ranks, card.rank)
	}

	sort.Sort(sort.Reverse(ByRank(ranks)))
	return ranks
}

func (h Hand) handType() HandType {
	switch {
	case h.isStraightFlush():
		return StraightFlush
	case h.isFourOfKind():
		return FourOfKind
	case h.isFullHouse():
		return FullHouse
	case h.isFlush():
		return Flush
	case h.isStraight():
		return Straight
	case h.isThreeOfKind():
		return ThreeOfKind
	case h.isTwoPairs():
		return TwoPairs
	case h.isTwoOfKind():
		return Pair
	default:
		return Nothing
	}
}

func (h Hand) String() string {
	return fmt.Sprintf("handType %v with cards %v", h.handType(), h.cards)
}

func (h Hand) isStraightFlush() bool {
	return h.isStraight() && h.isFlush()
}

func (h Hand) isFourOfKind() bool {
	rankToOccurences := h.getRankToOccurences()
	for _, occurences := range rankToOccurences {
		if occurences == 4 {
			return true
		}
	}
	return false
}

func (h Hand) isFullHouse() bool {
	rankToOccurences := h.getRankToOccurences()
	occurences := getValues(rankToOccurences)
	sort.Ints(occurences)
	fullHouse := []int{2, 3}
	return reflect.DeepEqual(occurences, fullHouse)
}

func (h Hand) isFlush() bool {
	suitToOccurences := h.getSuitToOccurences()
	for _, occurences := range suitToOccurences {
		if occurences != 5 {
			return false
		}
	}
	return true
}

func (h Hand) isStraight() bool {
	if h.isStraightStartingWithAce() {
		return true
	}

	ranks := h.Ranks()
	sort.Sort(ByRank(ranks))
	for i, rank := range ranks {
		if i == 0 {
			continue
		}
		if int(ranks[i-1]) != int(rank)-1 {
			return false
		}
	}
	return true
}

func (h Hand) isStraightStartingWithAce() bool {
	ranks := h.Ranks()
	sort.Sort(ByRank(ranks))
	straightStartingWithAce := []Rank{Two, Three, Four, Five, Ace}
	return reflect.DeepEqual(ranks, straightStartingWithAce)
}

func (h Hand) isThreeOfKind() bool {
	rankToOccurences := h.getRankToOccurences()
	for _, occurences := range rankToOccurences {
		if occurences == 3 {
			return true
		}
	}
	return false
}

func (h Hand) isTwoPairs() bool {
	rankToOccurences := h.getRankToOccurences()
	occurences := getValues(rankToOccurences)
	sort.Ints(occurences)
	twoPairs := []int{1, 2, 2}
	return reflect.DeepEqual(occurences, twoPairs)
}

func (h Hand) isTwoOfKind() bool {
	rankToOccurences := h.getRankToOccurences()
	for _, occurences := range rankToOccurences {
		if occurences == 2 {
			return true
		}
	}
	return false
}

func (h Hand) getRankToOccurences() (occurences map[Rank]int) {
	occurences = map[Rank]int{}
	for _, card := range h.cards {
		if _, ok := occurences[card.rank]; !ok {
			occurences[card.rank] = 0
		}
		occurences[card.rank] += 1
	}
	return occurences
}

func (h Hand) getSuitToOccurences() (occurences map[Suit]int) {
	occurences = map[Suit]int{}
	for _, card := range h.cards {
		if _, ok := occurences[card.suit]; !ok {
			occurences[card.suit] = 0
		}
		occurences[card.suit] += 1
	}
	return occurences
}

type ByScore []Hand

func (s ByScore) Len() int          { return len(s) }
func (s ByScore) Swap(a int, b int) { s[a], s[b] = s[b], s[a] }
func (s ByScore) Less(a int, b int) bool {
	handA := s[a]
	handB := s[b]
	comparison, err := handA.compare(handB)
	if err != nil {
		log.Fatal(err)
	}
	return comparison < 0
}

func getValues(rankToOccurences map[Rank]int) (values []int) {
	for _, v := range rankToOccurences {
		values = append(values, v)
	}
	return values
}
<|endoftext|>


<commit_before>package main

import "testing"

func TestGetBitsFromPacket(t *testing.T) {

	var bytePos int
	var bitPos int
	tests := []struct {
		name   string
		packet []byte
		bpP    uint
		ret    uint8
	}{
		{"24 Bits", []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 24, 255},
		{"12 Bits", []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 12, 240},
		{"9 Bits", []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 9, 14},
		{"3 Bits", []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 3, 1},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			// Reset position, as the stream of provided bits is limited
			bytePos = 0
			bitPos = 0
			res := getBitsFromPacket(tc.packet, &bytePos, &bitPos, tc.bpP)
			if res != tc.ret {
				t.Errorf("Input: %d Expected: %d \t Got %d", tc.packet, tc.ret, res)
			}
		})
	}
}

func TestCheckConfig(t *testing.T) {
	tests := []struct {
		name string
		cfg  configs
		err  string
	}{
		// Testing different output stiles
		{name: "Two Bits per Pixel", cfg: configs{2, 0, 0, 0, TERMINAL}, err: "-bits 2 is not divisible by three or one"},
		{name: "One Bit per Pixel", cfg: configs{1, 0, 0, 0, TERMINAL}},
		{name: "27 Bits per Pixel", cfg: configs{27, 0, 0, 0, TERMINAL}, err: "-bits 27 must be smaller than 25"},
		{name: "Terminal only", cfg: configs{3, 0, 0, 0, TERMINAL}},
		{name: "Terminal and Timeslize", cfg: configs{3, 0, 0, 0, (TERMINAL | TIMESLIZES)}, err: "-timeslize and -terminal can't be combined"},
		{name: "Fixed Slize", cfg: configs{1, 0, 0, 0, 0}},
		{name: "Time Slize", cfg: configs{1, 0, 50, 0, 0}},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			res := checkConfig(tc.cfg)

			if tc.err != "" {
				if res.Error() != tc.err {
					t.Errorf("Expected: %v \t Got: %v", tc.err, res)
				}
			}
		})
	}
}

func TestCreatePixel(t *testing.T) {
	tests := []struct {
		name   string
		packet []byte
		byteP  int
		bitP   int
		bpP    uint
		red    uint8
		green  uint8
		blue   uint8
	}{
		{"White", []byte{0xFF, 0xFF}, 0, 0, 1, 255, 255, 255},
		{"Black", []byte{0x00, 0x00}, 0, 0, 1, 0, 0, 0},
		{"Royal Blue", []byte{0x41, 0x69, 0xE1, 0x41, 0x69, 0xE1}, 0, 0, 24, 65, 105, 225},
	}
	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			c := createPixel(tc.packet, &(tc.byteP), &(tc.bitP), tc.bpP)
			r, g, b, _ := c.RGBA()
			if uint8(r) != tc.red || uint8(g) != tc.green || uint8(b) != tc.blue {
				t.Errorf("Expected: ", tc.red, tc.green, tc.blue, "\t Got: ", uint8(r), uint8(g), uint8(b))
			}
		})
	}
}
<commit_msg>Fix error message<commit_after>package main

import "testing"

func TestGetBitsFromPacket(t *testing.T) {

	var bytePos int
	var bitPos int
	tests := []struct {
		name   string
		packet []byte
		bpP    uint
		ret    uint8
	}{
		{"24 Bits", []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 24, 255},
		{"12 Bits", []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 12, 240},
		{"9 Bits", []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 9, 14},
		{"3 Bits", []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 3, 1},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			// Reset position, as the stream of provided bits is limited
			bytePos = 0
			bitPos = 0
			res := getBitsFromPacket(tc.packet, &bytePos, &bitPos, tc.bpP)
			if res != tc.ret {
				t.Errorf("Input: %d Expected: %d \t Got %d", tc.packet, tc.ret, res)
			}
		})
	}
}

func TestCheckConfig(t *testing.T) {
	tests := []struct {
		name string
		cfg  configs
		err  string
	}{
		// Testing different output stiles
		{name: "Two Bits per Pixel", cfg: configs{2, 0, 0, 0, TERMINAL}, err: "-bits 2 is not divisible by three or one"},
		{name: "One Bit per Pixel", cfg: configs{1, 0, 0, 0, TERMINAL}},
		{name: "27 Bits per Pixel", cfg: configs{27, 0, 0, 0, TERMINAL}, err: "-bits 27 must be smaller than 25"},
		{name: "Terminal only", cfg: configs{3, 0, 0, 0, TERMINAL}},
		{name: "Terminal and Timeslize", cfg: configs{3, 0, 0, 0, (TERMINAL | TIMESLIZES)}, err: "-timeslize and -terminal can't be combined"},
		{name: "Fixed Slize", cfg: configs{1, 0, 0, 0, 0}},
		{name: "Time Slize", cfg: configs{1, 0, 50, 0, 0}},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			res := checkConfig(tc.cfg)

			if tc.err != "" {
				if res.Error() != tc.err {
					t.Errorf("Expected: %v \t Got: %v", tc.err, res)
				}
			}
		})
	}
}

func TestCreatePixel(t *testing.T) {
	tests := []struct {
		name   string
		packet []byte
		byteP  int
		bitP   int
		bpP    uint
		red    uint8
		green  uint8
		blue   uint8
	}{
		{"White", []byte{0xFF, 0xFF}, 0, 0, 1, 255, 255, 255},
		{"Black", []byte{0x00, 0x00}, 0, 0, 1, 0, 0, 0},
		{"Royal Blue", []byte{0x41, 0x69, 0xE1, 0x41, 0x69, 0xE1}, 0, 0, 24, 65, 105, 225},
	}
	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			c := createPixel(tc.packet, &(tc.byteP), &(tc.bitP), tc.bpP)
			r, g, b, _ := c.RGBA()
			if uint8(r) != tc.red || uint8(g) != tc.green || uint8(b) != tc.blue {
				t.Errorf("Expected: r%dg%db%d\t Got: r%dg%db%d", tc.red, tc.green, tc.blue,uint8(r), uint8(g), uint8(b))
			}
		})
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2013 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 or at
// https://developers.google.com/open-source/licenses/bsd.

// golint lints the Go source files named on its command line.
package main

import (
	"flag"
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/golang/lint"
)

var (
	minConfidence = flag.Float64("min_confidence", 0.8, "minimum confidence of a problem to print it")
	setExitStatus = flag.Bool("set_exit_status", false, "set exit status to 1 if any issues are found")
	suggestions   int
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "\tgolint [flags] # runs on package in current directory\n")
	fmt.Fprintf(os.Stderr, "\tgolint [flags] [packages]\n")
	fmt.Fprintf(os.Stderr, "\tgolint [flags] [directories] # where a '/...' suffix includes all sub-directories\n")
	fmt.Fprintf(os.Stderr, "\tgolint [flags] [files] # all must belong to a single package\n")
	fmt.Fprintf(os.Stderr, "Flags:\n")
	flag.PrintDefaults()
}

func main() {
	flag.Usage = usage
	flag.Parse()

	if flag.NArg() == 0 {
		lintDir(".")
	} else {
		// dirsRun, filesRun, and pkgsRun indicate whether golint is applied to
		// directory, file or package targets. The distinction affects which
		// checks are run. It is no valid to mix target types.
		var dirsRun, filesRun, pkgsRun int
		var args []string
		for _, arg := range flag.Args() {
			if strings.HasSuffix(arg, "/...") && isDir(arg[:len(arg)-len("/...")]) {
				dirsRun = 1
				for _, dirname := range allPackagesInFS(arg) {
					args = append(args, dirname)
				}
			} else if isDir(arg) {
				dirsRun = 1
				args = append(args, arg)
			} else if exists(arg) {
				filesRun = 1
				args = append(args, arg)
			} else {
				pkgsRun = 1
				args = append(args, arg)
			}
		}

		if dirsRun+filesRun+pkgsRun != 1 {
			usage()
			os.Exit(2)
		}
		switch {
		case dirsRun == 1:
			for _, dir := range args {
				lintDir(dir)
			}
		case filesRun == 1:
			lintFiles(args...)
		case pkgsRun == 1:
			for _, pkg := range importPaths(args) {
				lintPackage(pkg)
			}
		}
	}

	if *setExitStatus && suggestions > 0 {
		fmt.Fprintf(os.Stderr, "Found %d lint suggestions; failing.\n", suggestions)
		os.Exit(1)
	}
}

func isDir(filename string) bool {
	fi, err := os.Stat(filename)
	return err == nil && fi.IsDir()
}

func exists(filename string) bool {
	_, err := os.Stat(filename)
	return err == nil
}

func lintFiles(filenames ...string) {
	files := make(map[string][]byte)
	for _, filename := range filenames {
		src, err := ioutil.ReadFile(filename)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			continue
		}
		files[filename] = src
	}

	l := new(lint.Linter)
	ps, err := l.LintFiles(files)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return
	}
	for _, p := range ps {
		if p.Confidence >= *minConfidence {
			fmt.Printf("%v: %s\n", p.Position, p.Text)
			suggestions++
		}
	}
}

func lintDir(dirname string) {
	pkg, err := build.ImportDir(dirname, 0)
	lintImportedPackage(pkg, err)
}

func lintPackage(pkgname string) {
	pkg, err := build.Import(pkgname, ".", 0)
	lintImportedPackage(pkg, err)
}

func lintImportedPackage(pkg *build.Package, err error) {
	if err != nil {
		if _, nogo := err.(*build.NoGoError); nogo {
			// Don't complain if the failure is due to no Go source files.
			return
		}
		fmt.Fprintln(os.Stderr, err)
		return
	}

	var files []string
	files = append(files, pkg.GoFiles...)
	files = append(files, pkg.CgoFiles...)
	files = append(files, pkg.TestGoFiles...)
	if pkg.Dir != "." {
		for i, f := range files {
			files[i] = filepath.Join(pkg.Dir, f)
		}
	}
	// TODO(dsymonds): Do foo_test too (pkg.XTestGoFiles)

	lintFiles(files...)
}
<commit_msg>golint: update import path from github to golang.org/x/lint<commit_after>// Copyright (c) 2013 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 or at
// https://developers.google.com/open-source/licenses/bsd.

// golint lints the Go source files named on its command line.
package main

import (
	"flag"
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"golang.org/x/lint"
)

var (
	minConfidence = flag.Float64("min_confidence", 0.8, "minimum confidence of a problem to print it")
	setExitStatus = flag.Bool("set_exit_status", false, "set exit status to 1 if any issues are found")
	suggestions   int
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "\tgolint [flags] # runs on package in current directory\n")
	fmt.Fprintf(os.Stderr, "\tgolint [flags] [packages]\n")
	fmt.Fprintf(os.Stderr, "\tgolint [flags] [directories] # where a '/...' suffix includes all sub-directories\n")
	fmt.Fprintf(os.Stderr, "\tgolint [flags] [files] # all must belong to a single package\n")
	fmt.Fprintf(os.Stderr, "Flags:\n")
	flag.PrintDefaults()
}

func main() {
	flag.Usage = usage
	flag.Parse()

	if flag.NArg() == 0 {
		lintDir(".")
	} else {
		// dirsRun, filesRun, and pkgsRun indicate whether golint is applied to
		// directory, file or package targets. The distinction affects which
		// checks are run. It is no valid to mix target types.
		var dirsRun, filesRun, pkgsRun int
		var args []string
		for _, arg := range flag.Args() {
			if strings.HasSuffix(arg, "/...") && isDir(arg[:len(arg)-len("/...")]) {
				dirsRun = 1
				for _, dirname := range allPackagesInFS(arg) {
					args = append(args, dirname)
				}
			} else if isDir(arg) {
				dirsRun = 1
				args = append(args, arg)
			} else if exists(arg) {
				filesRun = 1
				args = append(args, arg)
			} else {
				pkgsRun = 1
				args = append(args, arg)
			}
		}

		if dirsRun+filesRun+pkgsRun != 1 {
			usage()
			os.Exit(2)
		}
		switch {
		case dirsRun == 1:
			for _, dir := range args {
				lintDir(dir)
			}
		case filesRun == 1:
			lintFiles(args...)
		case pkgsRun == 1:
			for _, pkg := range importPaths(args) {
				lintPackage(pkg)
			}
		}
	}

	if *setExitStatus && suggestions > 0 {
		fmt.Fprintf(os.Stderr, "Found %d lint suggestions; failing.\n", suggestions)
		os.Exit(1)
	}
}

func isDir(filename string) bool {
	fi, err := os.Stat(filename)
	return err == nil && fi.IsDir()
}

func exists(filename string) bool {
	_, err := os.Stat(filename)
	return err == nil
}

func lintFiles(filenames ...string) {
	files := make(map[string][]byte)
	for _, filename := range filenames {
		src, err := ioutil.ReadFile(filename)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			continue
		}
		files[filename] = src
	}

	l := new(lint.Linter)
	ps, err := l.LintFiles(files)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return
	}
	for _, p := range ps {
		if p.Confidence >= *minConfidence {
			fmt.Printf("%v: %s\n", p.Position, p.Text)
			suggestions++
		}
	}
}

func lintDir(dirname string) {
	pkg, err := build.ImportDir(dirname, 0)
	lintImportedPackage(pkg, err)
}

func lintPackage(pkgname string) {
	pkg, err := build.Import(pkgname, ".", 0)
	lintImportedPackage(pkg, err)
}

func lintImportedPackage(pkg *build.Package, err error) {
	if err != nil {
		if _, nogo := err.(*build.NoGoError); nogo {
			// Don't complain if the failure is due to no Go source files.
			return
		}
		fmt.Fprintln(os.Stderr, err)
		return
	}

	var files []string
	files = append(files, pkg.GoFiles...)
	files = append(files, pkg.CgoFiles...)
	files = append(files, pkg.TestGoFiles...)
	if pkg.Dir != "." {
		for i, f := range files {
			files[i] = filepath.Join(pkg.Dir, f)
		}
	}
	// TODO(dsymonds): Do foo_test too (pkg.XTestGoFiles)

	lintFiles(files...)
}
<|endoftext|>


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

// Package google provides support for making
// OAuth2 authorized and authenticated HTTP requests
// to Google APIs. It supports Web server, client-side,
// service accounts, Google Compute Engine service accounts,
// and Google App Engine service accounts authorization
// and authentications flows:
//
// For more information, please read
// https://developers.google.com/accounts/docs/OAuth2.
package google

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"time"

	"github.com/golang/oauth2"
)

const (
	// Google endpoints.
	uriGoogleAuth  = "https://accounts.google.com/o/oauth2/auth"
	uriGoogleToken = "https://accounts.google.com/o/oauth2/token"
)

type metaTokenRespBody struct {
	AccessToken string        `json:"access_token"`
	ExpiresIn   time.Duration `json:"expires_in"`
	TokenType   string        `json:"token_type"`
}

// ComputeEngineConfig represents a OAuth 2.0 consumer client
// running on Google Compute Engine.
type ComputeEngineConfig struct {
	// Client is the HTTP client to be used to retrieve
	// tokens from the OAuth 2.0 provider.
	Client *http.Client

	// Transport is the round tripper to be used
	// to construct new oauth2.Transport instances from
	// this configuration.
	Transport http.RoundTripper

	account string
}

// NewConfig creates a new OAuth2 config that uses Google
// endpoints.
func NewConfig(opts *oauth2.Options) (*oauth2.Config, error) {
	return oauth2.NewConfig(opts, uriGoogleAuth, uriGoogleToken)
}

// NewServiceAccountConfig creates a new JWT config that can
// fetch Bearer JWT tokens from Google endpoints.
func NewServiceAccountConfig(opts *oauth2.JWTOptions) (*oauth2.JWTConfig, error) {
	return oauth2.NewJWTConfig(opts, uriGoogleToken)
}

// NewServiceAccountJSONConfig creates a new JWT config from a
// JSON key file downloaded from the Google Developers Console.
// See the "Credentials" page under "APIs & Auth" for your project
// at https://console.developers.google.com.
func NewServiceAccountJSONConfig(filename string, scopes ...string) (*oauth2.JWTConfig, error) {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	var key struct {
		Email      string `json:"client_email"`
		PrivateKey string `json:"private_key"`
	}
	if err := json.Unmarshal(b, &key); err != nil {
		return nil, err
	}
	opts := &oauth2.JWTOptions{
		Email:      key.Email,
		PrivateKey: []byte(key.PrivateKey),
		Scopes:     scopes,
	}
	return NewServiceAccountConfig(opts)
}

// NewComputeEngineConfig creates a new config that can fetch tokens
// from Google Compute Engine instance's metaserver. If no account is
// provided, default is used.
func NewComputeEngineConfig(account string) *ComputeEngineConfig {
	return &ComputeEngineConfig{account: account}
}

// NewTransport creates an authorized transport.
func (c *ComputeEngineConfig) NewTransport() *oauth2.Transport {
	return oauth2.NewTransport(c.transport(), c, nil)
}

// FetchToken retrieves a new access token via metadata server.
func (c *ComputeEngineConfig) FetchToken(existing *oauth2.Token) (token *oauth2.Token, err error) {
	account := "default"
	if c.account != "" {
		account = c.account
	}
	u := "http://metadata/computeMetadata/v1/instance/service-accounts/" + account + "/token"
	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return
	}
	req.Header.Add("X-Google-Metadata-Request", "True")
	resp, err := c.client().Do(req)
	if err != nil {
		return
	}
	defer resp.Body.Close()
	var tokenResp metaTokenRespBody
	err = json.NewDecoder(resp.Body).Decode(&tokenResp)
	if err != nil {
		return
	}
	token = &oauth2.Token{
		AccessToken: tokenResp.AccessToken,
		TokenType:   tokenResp.TokenType,
		Expiry:      time.Now().Add(tokenResp.ExpiresIn * time.Second),
	}
	return
}

func (c *ComputeEngineConfig) transport() http.RoundTripper {
	if c.Transport != nil {
		return c.Transport
	}
	return http.DefaultTransport
}

func (c *ComputeEngineConfig) client() *http.Client {
	if c.Client != nil {
		return c.Client
	}
	return http.DefaultClient
}
<commit_msg>Update metadata server URL to be a FQDN.<commit_after>// Copyright 2014 The oauth2 Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package google provides support for making
// OAuth2 authorized and authenticated HTTP requests
// to Google APIs. It supports Web server, client-side,
// service accounts, Google Compute Engine service accounts,
// and Google App Engine service accounts authorization
// and authentications flows:
//
// For more information, please read
// https://developers.google.com/accounts/docs/OAuth2.
package google

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"time"

	"github.com/golang/oauth2"
)

const (
	// Google endpoints.
	uriGoogleAuth  = "https://accounts.google.com/o/oauth2/auth"
	uriGoogleToken = "https://accounts.google.com/o/oauth2/token"
)

type metaTokenRespBody struct {
	AccessToken string        `json:"access_token"`
	ExpiresIn   time.Duration `json:"expires_in"`
	TokenType   string        `json:"token_type"`
}

// ComputeEngineConfig represents a OAuth 2.0 consumer client
// running on Google Compute Engine.
type ComputeEngineConfig struct {
	// Client is the HTTP client to be used to retrieve
	// tokens from the OAuth 2.0 provider.
	Client *http.Client

	// Transport is the round tripper to be used
	// to construct new oauth2.Transport instances from
	// this configuration.
	Transport http.RoundTripper

	account string
}

// NewConfig creates a new OAuth2 config that uses Google
// endpoints.
func NewConfig(opts *oauth2.Options) (*oauth2.Config, error) {
	return oauth2.NewConfig(opts, uriGoogleAuth, uriGoogleToken)
}

// NewServiceAccountConfig creates a new JWT config that can
// fetch Bearer JWT tokens from Google endpoints.
func NewServiceAccountConfig(opts *oauth2.JWTOptions) (*oauth2.JWTConfig, error) {
	return oauth2.NewJWTConfig(opts, uriGoogleToken)
}

// NewServiceAccountJSONConfig creates a new JWT config from a
// JSON key file downloaded from the Google Developers Console.
// See the "Credentials" page under "APIs & Auth" for your project
// at https://console.developers.google.com.
func NewServiceAccountJSONConfig(filename string, scopes ...string) (*oauth2.JWTConfig, error) {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	var key struct {
		Email      string `json:"client_email"`
		PrivateKey string `json:"private_key"`
	}
	if err := json.Unmarshal(b, &key); err != nil {
		return nil, err
	}
	opts := &oauth2.JWTOptions{
		Email:      key.Email,
		PrivateKey: []byte(key.PrivateKey),
		Scopes:     scopes,
	}
	return NewServiceAccountConfig(opts)
}

// NewComputeEngineConfig creates a new config that can fetch tokens
// from Google Compute Engine instance's metaserver. If no account is
// provided, default is used.
func NewComputeEngineConfig(account string) *ComputeEngineConfig {
	return &ComputeEngineConfig{account: account}
}

// NewTransport creates an authorized transport.
func (c *ComputeEngineConfig) NewTransport() *oauth2.Transport {
	return oauth2.NewTransport(c.transport(), c, nil)
}

// FetchToken retrieves a new access token via metadata server.
func (c *ComputeEngineConfig) FetchToken(existing *oauth2.Token) (token *oauth2.Token, err error) {
	account := "default"
	if c.account != "" {
		account = c.account
	}
	u := "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/" + account + "/token"
	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return
	}
	req.Header.Add("X-Google-Metadata-Request", "True")
	resp, err := c.client().Do(req)
	if err != nil {
		return
	}
	defer resp.Body.Close()
	var tokenResp metaTokenRespBody
	err = json.NewDecoder(resp.Body).Decode(&tokenResp)
	if err != nil {
		return
	}
	token = &oauth2.Token{
		AccessToken: tokenResp.AccessToken,
		TokenType:   tokenResp.TokenType,
		Expiry:      time.Now().Add(tokenResp.ExpiresIn * time.Second),
	}
	return
}

func (c *ComputeEngineConfig) transport() http.RoundTripper {
	if c.Transport != nil {
		return c.Transport
	}
	return http.DefaultTransport
}

func (c *ComputeEngineConfig) client() *http.Client {
	if c.Client != nil {
		return c.Client
	}
	return http.DefaultClient
}
<|endoftext|>


<commit_before>/*
Package gothic wraps common behaviour when using Goth. This makes it quick, and easy, to get up
and running with Goth. Of course, if you want complete control over how things flow, in regards
to the authentication process, feel free and use Goth directly.

See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
package gothic

import (
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"github.com/markbates/goth"
)

// SessionName is the key used to access the session store.
const SessionName = "_gothic_session"

// Store can/should be set by applications using gothic. The default is a cookie store.
var Store sessions.Store
var defaultStore sessions.Store

var keySet = false

var gothicRand *rand.Rand

func init() {
	key := []byte(os.Getenv("SESSION_SECRET"))
	keySet = len(key) != 0

	cookieStore := sessions.NewCookieStore([]byte(key))
	cookieStore.Options.HttpOnly = true
	Store = cookieStore
	defaultStore = Store
	gothicRand = rand.New(rand.NewSource(time.Now().UnixNano()))
}

/*
BeginAuthHandler is a convenience handler for starting the authentication process.
It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

BeginAuthHandler will redirect the user to the appropriate authentication end-point
for the requested provider.

See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
func BeginAuthHandler(res http.ResponseWriter, req *http.Request) {
	url, err := GetAuthURL(res, req)
	if err != nil {
		res.WriteHeader(http.StatusBadRequest)
		fmt.Fprintln(res, err)
		return
	}

	http.Redirect(res, req, url, http.StatusTemporaryRedirect)
}

// SetState sets the state string associated with the given request.
// If no state string is associated with the request, one will be generated.
// This state is sent to the provider and can be retrieved during the
// callback.
var SetState = func(req *http.Request) string {
	state := req.URL.Query().Get("state")
	if len(state) > 0 {
		return state
	}

	// If a state query param is not passed in, generate a random
	// base64-encoded nonce so that the state on the auth URL
	// is unguessable, preventing CSRF attacks, as described in
	//
	// https://auth0.com/docs/protocols/oauth2/oauth-state#keep-reading
	nonceBytes := make([]byte, 64)
	for i := 0; i < 64; i++ {
		nonceBytes[i] = byte(gothicRand.Int63() % 256)
	}
	return base64.URLEncoding.EncodeToString(nonceBytes)
}

// GetState gets the state returned by the provider during the callback.
// This is used to prevent CSRF attacks, see
// http://tools.ietf.org/html/rfc6749#section-10.12
var GetState = func(req *http.Request) string {
	return req.URL.Query().Get("state")
}

/*
GetAuthURL starts the authentication process with the requested provided.
It will return a URL that should be used to send users to.

It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

I would recommend using the BeginAuthHandler instead of doing all of these steps
yourself, but that's entirely up to you.
*/
func GetAuthURL(res http.ResponseWriter, req *http.Request) (string, error) {
	if !keySet && defaultStore == Store {
		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
	}

	providerName, err := GetProviderName(req)
	if err != nil {
		return "", err
	}

	provider, err := goth.GetProvider(providerName)
	if err != nil {
		return "", err
	}
	sess, err := provider.BeginAuth(SetState(req))
	if err != nil {
		return "", err
	}

	url, err := sess.GetAuthURL()
	if err != nil {
		return "", err
	}

	err = storeInSession(providerName, sess.Marshal(), req, res)

	if err != nil {
		return "", err
	}

	return url, err
}

/*
CompleteUserAuth does what it says on the tin. It completes the authentication
process and fetches all of the basic information about the user from the provider.

It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.User, error) {
	defer Logout(res, req)
	if !keySet && defaultStore == Store {
		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
	}

	providerName, err := GetProviderName(req)
	if err != nil {
		return goth.User{}, err
	}

	provider, err := goth.GetProvider(providerName)
	if err != nil {
		return goth.User{}, err
	}

	value, err := getFromSession(providerName, req)
	if err != nil {
		return goth.User{}, err
	}

	sess, err := provider.UnmarshalSession(value)
	if err != nil {
		return goth.User{}, err
	}

	err = validateState(req, sess)
	if err != nil {
		return goth.User{}, err
	}

	user, err := provider.FetchUser(sess)
	if err == nil {
		// user can be found with existing session data
		return user, err
	}

	// get new token and retry fetch
	_, err = sess.Authorize(provider, req.URL.Query())
	if err != nil {
		return goth.User{}, err
	}

	err = storeInSession(providerName, sess.Marshal(), req, res)

	if err != nil {
		return goth.User{}, err
	}

	gu, err := provider.FetchUser(sess)
	return gu, err
}

// validateState ensures that the state token param from the original
// AuthURL matches the one included in the current (callback) request.
func validateState(req *http.Request, sess goth.Session) error {
	rawAuthURL, err := sess.GetAuthURL()
	if err != nil {
		return err
	}

	authURL, err := url.Parse(rawAuthURL)
	if err != nil {
		return err
	}

	originalState := authURL.Query().Get("state")
	if originalState != "" && (originalState != req.URL.Query().Get("state")) {
		return errors.New("state token mismatch")
	}
	return nil
}

// Logout invalidates a user session.
func Logout(res http.ResponseWriter, req *http.Request) error {
	session, err := Store.Get(req, SessionName)
	if err != nil {
		return err
	}
	session.Options.MaxAge = -1
	session.Values = make(map[interface{}]interface{})
	err = session.Save(req, res)
	if err != nil {
		return errors.New("Could not delete user session ")
	}
	return nil
}

// GetProviderName is a function used to get the name of a provider
// for a given request. By default, this provider is fetched from
// the URL query string. If you provide it in a different way,
// assign your own function to this variable that returns the provider
// name for your request.
var GetProviderName = getProviderName

func getProviderName(req *http.Request) (string, error) {

	// get all the used providers
	providers := goth.GetProviders()

	// loop over the used providers, if we already have a valid session for any provider (ie. user is already logged-in with a provider), then return that provider name
	for _, provider := range providers {
		p := provider.Name()
		session, _ := Store.Get(req, p+SessionName)
		value := session.Values[p]
		if _, ok := value.(string); ok {
			return p, nil
		}
	}

	// try to get it from the url param "provider"
	if p := req.URL.Query().Get("provider"); p != "" {
		return p, nil
	}

	// try to get it from the url param ":provider"
	if p := req.URL.Query().Get(":provider"); p != "" {
		return p, nil
	}

	// try to get it from the context's value of "provider" key
	if p, ok := mux.Vars(req)["provider"]; ok {
		return p, nil
	}

	//  try to get it from the go-context's value of "provider" key
	if p, ok := req.Context().Value("provider").(string); ok {
		return p, nil
	}

	// if not found then return an empty string with the corresponding error
	return "", errors.New("you must select a provider")
}

func storeInSession(key string, value string, req *http.Request, res http.ResponseWriter) error {
	session, _ := Store.Get(req, SessionName)

	if err := updateSessionValue(session, key, value); err != nil {
		return err
	}

	return session.Save(req, res)
}

func getFromSession(key string, req *http.Request) (string, error) {
	session, _ := Store.Get(req, SessionName)
	value, err := getSessionValue(session, key)
	if err != nil {
		return "", errors.New("could not find a matching session for this request")
	}

	return value, nil
}

func getSessionValue(session *sessions.Session, key string) (string, error) {
	value := session.Values[key]
	if value == nil {
		return "", fmt.Errorf("could not find a matching session for this request")
	}

	rdata := strings.NewReader(value.(string))
	r, err := gzip.NewReader(rdata)
	if err != nil {
		return "", err
	}
	s, err := ioutil.ReadAll(r)
	if err != nil {
		return "", err
	}

	return string(s), nil
}

func updateSessionValue(session *sessions.Session, key, value string) error {
	var b bytes.Buffer
	gz := gzip.NewWriter(&b)
	if _, err := gz.Write([]byte(value)); err != nil {
		return err
	}
	if err := gz.Flush(); err != nil {
		return err
	}
	if err := gz.Close(); err != nil {
		return err
	}

	session.Values[key] = b.String()
	return nil
}
<commit_msg>Export helpful session access methods<commit_after>/*
Package gothic wraps common behaviour when using Goth. This makes it quick, and easy, to get up
and running with Goth. Of course, if you want complete control over how things flow, in regards
to the authentication process, feel free and use Goth directly.

See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
package gothic

import (
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"github.com/markbates/goth"
)

// SessionName is the key used to access the session store.
const SessionName = "_gothic_session"

// Store can/should be set by applications using gothic. The default is a cookie store.
var Store sessions.Store
var defaultStore sessions.Store

var keySet = false

var gothicRand *rand.Rand

func init() {
	key := []byte(os.Getenv("SESSION_SECRET"))
	keySet = len(key) != 0

	cookieStore := sessions.NewCookieStore([]byte(key))
	cookieStore.Options.HttpOnly = true
	Store = cookieStore
	defaultStore = Store
	gothicRand = rand.New(rand.NewSource(time.Now().UnixNano()))
}

/*
BeginAuthHandler is a convenience handler for starting the authentication process.
It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

BeginAuthHandler will redirect the user to the appropriate authentication end-point
for the requested provider.

See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
func BeginAuthHandler(res http.ResponseWriter, req *http.Request) {
	url, err := GetAuthURL(res, req)
	if err != nil {
		res.WriteHeader(http.StatusBadRequest)
		fmt.Fprintln(res, err)
		return
	}

	http.Redirect(res, req, url, http.StatusTemporaryRedirect)
}

// SetState sets the state string associated with the given request.
// If no state string is associated with the request, one will be generated.
// This state is sent to the provider and can be retrieved during the
// callback.
var SetState = func(req *http.Request) string {
	state := req.URL.Query().Get("state")
	if len(state) > 0 {
		return state
	}

	// If a state query param is not passed in, generate a random
	// base64-encoded nonce so that the state on the auth URL
	// is unguessable, preventing CSRF attacks, as described in
	//
	// https://auth0.com/docs/protocols/oauth2/oauth-state#keep-reading
	nonceBytes := make([]byte, 64)
	for i := 0; i < 64; i++ {
		nonceBytes[i] = byte(gothicRand.Int63() % 256)
	}
	return base64.URLEncoding.EncodeToString(nonceBytes)
}

// GetState gets the state returned by the provider during the callback.
// This is used to prevent CSRF attacks, see
// http://tools.ietf.org/html/rfc6749#section-10.12
var GetState = func(req *http.Request) string {
	return req.URL.Query().Get("state")
}

/*
GetAuthURL starts the authentication process with the requested provided.
It will return a URL that should be used to send users to.

It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

I would recommend using the BeginAuthHandler instead of doing all of these steps
yourself, but that's entirely up to you.
*/
func GetAuthURL(res http.ResponseWriter, req *http.Request) (string, error) {
	if !keySet && defaultStore == Store {
		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
	}

	providerName, err := GetProviderName(req)
	if err != nil {
		return "", err
	}

	provider, err := goth.GetProvider(providerName)
	if err != nil {
		return "", err
	}
	sess, err := provider.BeginAuth(SetState(req))
	if err != nil {
		return "", err
	}

	url, err := sess.GetAuthURL()
	if err != nil {
		return "", err
	}

	err = StoreInSession(providerName, sess.Marshal(), req, res)

	if err != nil {
		return "", err
	}

	return url, err
}

/*
CompleteUserAuth does what it says on the tin. It completes the authentication
process and fetches all of the basic information about the user from the provider.

It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.User, error) {
	defer Logout(res, req)
	if !keySet && defaultStore == Store {
		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
	}

	providerName, err := GetProviderName(req)
	if err != nil {
		return goth.User{}, err
	}

	provider, err := goth.GetProvider(providerName)
	if err != nil {
		return goth.User{}, err
	}

	value, err := GetFromSession(providerName, req)
	if err != nil {
		return goth.User{}, err
	}

	sess, err := provider.UnmarshalSession(value)
	if err != nil {
		return goth.User{}, err
	}

	err = validateState(req, sess)
	if err != nil {
		return goth.User{}, err
	}

	user, err := provider.FetchUser(sess)
	if err == nil {
		// user can be found with existing session data
		return user, err
	}

	// get new token and retry fetch
	_, err = sess.Authorize(provider, req.URL.Query())
	if err != nil {
		return goth.User{}, err
	}

	err = StoreInSession(providerName, sess.Marshal(), req, res)

	if err != nil {
		return goth.User{}, err
	}

	gu, err := provider.FetchUser(sess)
	return gu, err
}

// validateState ensures that the state token param from the original
// AuthURL matches the one included in the current (callback) request.
func validateState(req *http.Request, sess goth.Session) error {
	rawAuthURL, err := sess.GetAuthURL()
	if err != nil {
		return err
	}

	authURL, err := url.Parse(rawAuthURL)
	if err != nil {
		return err
	}

	originalState := authURL.Query().Get("state")
	if originalState != "" && (originalState != req.URL.Query().Get("state")) {
		return errors.New("state token mismatch")
	}
	return nil
}

// Logout invalidates a user session.
func Logout(res http.ResponseWriter, req *http.Request) error {
	session, err := Store.Get(req, SessionName)
	if err != nil {
		return err
	}
	session.Options.MaxAge = -1
	session.Values = make(map[interface{}]interface{})
	err = session.Save(req, res)
	if err != nil {
		return errors.New("Could not delete user session ")
	}
	return nil
}

// GetProviderName is a function used to get the name of a provider
// for a given request. By default, this provider is fetched from
// the URL query string. If you provide it in a different way,
// assign your own function to this variable that returns the provider
// name for your request.
var GetProviderName = getProviderName

func getProviderName(req *http.Request) (string, error) {

	// get all the used providers
	providers := goth.GetProviders()

	// loop over the used providers, if we already have a valid session for any provider (ie. user is already logged-in with a provider), then return that provider name
	for _, provider := range providers {
		p := provider.Name()
		session, _ := Store.Get(req, p+SessionName)
		value := session.Values[p]
		if _, ok := value.(string); ok {
			return p, nil
		}
	}

	// try to get it from the url param "provider"
	if p := req.URL.Query().Get("provider"); p != "" {
		return p, nil
	}

	// try to get it from the url param ":provider"
	if p := req.URL.Query().Get(":provider"); p != "" {
		return p, nil
	}

	// try to get it from the context's value of "provider" key
	if p, ok := mux.Vars(req)["provider"]; ok {
		return p, nil
	}

	//  try to get it from the go-context's value of "provider" key
	if p, ok := req.Context().Value("provider").(string); ok {
		return p, nil
	}

	// if not found then return an empty string with the corresponding error
	return "", errors.New("you must select a provider")
}

func StoreInSession(key string, value string, req *http.Request, res http.ResponseWriter) error {
	session, _ := Store.Get(req, SessionName)

	if err := updateSessionValue(session, key, value); err != nil {
		return err
	}

	return session.Save(req, res)
}

func GetFromSession(key string, req *http.Request) (string, error) {
	session, _ := Store.Get(req, SessionName)
	value, err := getSessionValue(session, key)
	if err != nil {
		return "", errors.New("could not find a matching session for this request")
	}

	return value, nil
}

func getSessionValue(session *sessions.Session, key string) (string, error) {
	value := session.Values[key]
	if value == nil {
		return "", fmt.Errorf("could not find a matching session for this request")
	}

	rdata := strings.NewReader(value.(string))
	r, err := gzip.NewReader(rdata)
	if err != nil {
		return "", err
	}
	s, err := ioutil.ReadAll(r)
	if err != nil {
		return "", err
	}

	return string(s), nil
}

func updateSessionValue(session *sessions.Session, key, value string) error {
	var b bytes.Buffer
	gz := gzip.NewWriter(&b)
	if _, err := gz.Write([]byte(value)); err != nil {
		return err
	}
	if err := gz.Flush(); err != nil {
		return err
	}
	if err := gz.Close(); err != nil {
		return err
	}

	session.Values[key] = b.String()
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"runtime"

	"github.com/omakoto/yt-up/oauth"
)

const missingClientSecretsMessage = `
Please configure OAuth 2.0

To make this sample run, you need to populate the client_secrets.json file
found at:

   %v

with information from the {{ Google Cloud Console }}
{{ https://cloud.google.com/console }}

For more information about the client_secrets.json file format, please visit:
https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
`

var (
	clientSecretsFile = flag.String("secrets", "client_secrets.json", "Client Secrets configuration")
	cacheFile         = flag.String("cache", "", "Token cache file")
)

// ClientConfig is a data structure definition for the client_secrets.json file.
// The code unmarshals the JSON configuration file into this structure.
type ClientConfig struct {
	ClientID     string   `json:"client_id"`
	ClientSecret string   `json:"client_secret"`
	RedirectURIs []string `json:"redirect_uris"`
	AuthURI      string   `json:"auth_uri"`
	TokenURI     string   `json:"token_uri"`
}

// Config is a root-level configuration object.
type Config struct {
	Installed ClientConfig `json:"installed"`
	Web       ClientConfig `json:"web"`
}

// openURL opens a browser window to the specified location.
// This code originally appeared at:
//   http://stackoverflow.com/questions/10377243/how-can-i-launch-a-process-that-is-not-a-file-in-go
func openURL(url string) error {
	var err error
	switch runtime.GOOS {
	case "linux":
		err = exec.Command("xdg-open", url).Start()
	case "windows":
		err = exec.Command("rundll32", "url.dll,FileProtocolHandler", "http://localhost:4001/").Start()
	case "darwin":
		err = exec.Command("open", url).Start()
	default:
		err = fmt.Errorf("Cannot open URL %s on this platform", url)
	}
	return err
}

func getHomeDir() string {
	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}
	return usr.HomeDir
}

// readConfig reads the configuration from clientSecretsFile.
// It returns an oauth configuration object for use with the Google API client.
func readConfig(scope string) (*oauth.Config, error) {
	// Read the secrets file
	data, err := ioutil.ReadFile(*clientSecretsFile)
	if err != nil {
		pwd, _ := os.Getwd()
		fullPath := filepath.Join(pwd, *clientSecretsFile)
		return nil, fmt.Errorf(missingClientSecretsMessage, fullPath)
	}

	cfg := new(Config)
	err = json.Unmarshal(data, &cfg)
	if err != nil {
		return nil, err
	}

	return &oauth.Config{
		ClientId:     cfg.Installed.ClientID,
		ClientSecret: cfg.Installed.ClientSecret,
		Scope:        scope,
		AuthURL:      cfg.Installed.AuthURI,
		TokenURL:     cfg.Installed.TokenURI,
		RedirectURL:  "http://localhost:8080/",
		TokenCache:   oauth.CacheFile(getHomeDir() + "/.yt-up.oauth.cache"),
		// Get a refresh token so we can use the access token indefinitely
		AccessType: "offline",
		// If we want a refresh token, we must set this attribute
		// to force an approval prompt or the code won't work.
		ApprovalPrompt: "force",
	}, nil
}

// startWebServer starts a web server that listens on http://localhost:8080.
// The webserver waits for an oauth code in the three-legged auth flow.
func startWebServer() (codeCh chan string, err error) {
	listener, err := net.Listen("tcp", "localhost:8080")
	if err != nil {
		return nil, err
	}
	codeCh = make(chan string)
	go http.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		code := r.FormValue("code")
		codeCh <- code // send code to OAuth flow
		listener.Close()
		w.Header().Set("Content-Type", "text/plain")
		fmt.Fprintf(w, "Received code: %v\r\nYou can now safely close this browser window.", code)
	}))

	return codeCh, nil
}

// buildOAuthHTTPClient takes the user through the three-legged OAuth flow.
// It opens a browser in the native OS or outputs a URL, then blocks until
// the redirect completes to the /oauth2callback URI.
// It returns an instance of an HTTP client that can be passed to the
// constructor of the YouTube client.
func buildOAuthHTTPClient(scope string) (*http.Client, error) {
	config, err := readConfig(scope)
	if err != nil {
		msg := fmt.Sprintf("Cannot read configuration file: %v", err)
		return nil, errors.New(msg)
	}

	transport := &oauth.Transport{Config: config}

	// Try to read the token from the cache file.
	// If an error occurs, do the three-legged OAuth flow because
	// the token is invalid or doesn't exist.
	token, err := config.TokenCache.Token()
	if err != nil {
		// Start web server.
		// This is how this program receives the authorization code
		// when the browser redirects.
		codeCh, err := startWebServer()
		if err != nil {
			return nil, err
		}

		// Open url in browser
		url := config.AuthCodeURL("")
		// fmt.Println("URL=" + url)
		err = openURL(url)
		if err != nil {
			log.Println("Visit the URL below to get a code.",
				" This program will pause until the site is visted.")
		} else {
			log.Println("Your browser has been opened to an authorization URL.",
				" This program will resume once authorization has been provided.")
		}
		// fmt.Println(url)

		// Wait for the web server to get the code.
		code := <-codeCh

		// This code caches the authorization code on the local
		// filesystem, if necessary, as long as the TokenCache
		// attribute in the config is set.
		token, err = transport.Exchange(code)
		if err != nil {
			return nil, err
		}
	}

	transport.Token = token
	return transport.Client(), nil
}
<commit_msg>remove stale flag<commit_after>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"runtime"

	"github.com/omakoto/yt-up/oauth"
)

const missingClientSecretsMessage = `
Please configure OAuth 2.0

To make this sample run, you need to populate the client_secrets.json file
found at:

   %v

with information from the {{ Google Cloud Console }}
{{ https://cloud.google.com/console }}

For more information about the client_secrets.json file format, please visit:
https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
`

var (
	clientSecretsFile = flag.String("secrets", "client_secrets.json", "Client Secrets configuration")
)

// ClientConfig is a data structure definition for the client_secrets.json file.
// The code unmarshals the JSON configuration file into this structure.
type ClientConfig struct {
	ClientID     string   `json:"client_id"`
	ClientSecret string   `json:"client_secret"`
	RedirectURIs []string `json:"redirect_uris"`
	AuthURI      string   `json:"auth_uri"`
	TokenURI     string   `json:"token_uri"`
}

// Config is a root-level configuration object.
type Config struct {
	Installed ClientConfig `json:"installed"`
	Web       ClientConfig `json:"web"`
}

// openURL opens a browser window to the specified location.
// This code originally appeared at:
//   http://stackoverflow.com/questions/10377243/how-can-i-launch-a-process-that-is-not-a-file-in-go
func openURL(url string) error {
	var err error
	switch runtime.GOOS {
	case "linux":
		err = exec.Command("xdg-open", url).Start()
	case "windows":
		err = exec.Command("rundll32", "url.dll,FileProtocolHandler", "http://localhost:4001/").Start()
	case "darwin":
		err = exec.Command("open", url).Start()
	default:
		err = fmt.Errorf("Cannot open URL %s on this platform", url)
	}
	return err
}

func getHomeDir() string {
	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}
	return usr.HomeDir
}

// readConfig reads the configuration from clientSecretsFile.
// It returns an oauth configuration object for use with the Google API client.
func readConfig(scope string) (*oauth.Config, error) {
	// Read the secrets file
	data, err := ioutil.ReadFile(*clientSecretsFile)
	if err != nil {
		pwd, _ := os.Getwd()
		fullPath := filepath.Join(pwd, *clientSecretsFile)
		return nil, fmt.Errorf(missingClientSecretsMessage, fullPath)
	}

	cfg := new(Config)
	err = json.Unmarshal(data, &cfg)
	if err != nil {
		return nil, err
	}

	return &oauth.Config{
		ClientId:     cfg.Installed.ClientID,
		ClientSecret: cfg.Installed.ClientSecret,
		Scope:        scope,
		AuthURL:      cfg.Installed.AuthURI,
		TokenURL:     cfg.Installed.TokenURI,
		RedirectURL:  "http://localhost:8080/",
		TokenCache:   oauth.CacheFile(getHomeDir() + "/.yt-up.oauth.cache"),
		// Get a refresh token so we can use the access token indefinitely
		AccessType: "offline",
		// If we want a refresh token, we must set this attribute
		// to force an approval prompt or the code won't work.
		ApprovalPrompt: "force",
	}, nil
}

// startWebServer starts a web server that listens on http://localhost:8080.
// The webserver waits for an oauth code in the three-legged auth flow.
func startWebServer() (codeCh chan string, err error) {
	listener, err := net.Listen("tcp", "localhost:8080")
	if err != nil {
		return nil, err
	}
	codeCh = make(chan string)
	go http.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		code := r.FormValue("code")
		codeCh <- code // send code to OAuth flow
		listener.Close()
		w.Header().Set("Content-Type", "text/plain")
		fmt.Fprintf(w, "Received code: %v\r\nYou can now safely close this browser window.", code)
	}))

	return codeCh, nil
}

// buildOAuthHTTPClient takes the user through the three-legged OAuth flow.
// It opens a browser in the native OS or outputs a URL, then blocks until
// the redirect completes to the /oauth2callback URI.
// It returns an instance of an HTTP client that can be passed to the
// constructor of the YouTube client.
func buildOAuthHTTPClient(scope string) (*http.Client, error) {
	config, err := readConfig(scope)
	if err != nil {
		msg := fmt.Sprintf("Cannot read configuration file: %v", err)
		return nil, errors.New(msg)
	}

	transport := &oauth.Transport{Config: config}

	// Try to read the token from the cache file.
	// If an error occurs, do the three-legged OAuth flow because
	// the token is invalid or doesn't exist.
	token, err := config.TokenCache.Token()
	if err != nil {
		// Start web server.
		// This is how this program receives the authorization code
		// when the browser redirects.
		codeCh, err := startWebServer()
		if err != nil {
			return nil, err
		}

		// Open url in browser
		url := config.AuthCodeURL("")
		// fmt.Println("URL=" + url)
		err = openURL(url)
		if err != nil {
			log.Println("Visit the URL below to get a code.",
				" This program will pause until the site is visted.")
		} else {
			log.Println("Your browser has been opened to an authorization URL.",
				" This program will resume once authorization has been provided.")
		}
		// fmt.Println(url)

		// Wait for the web server to get the code.
		code := <-codeCh

		// This code caches the authorization code on the local
		// filesystem, if necessary, as long as the TokenCache
		// attribute in the config is set.
		token, err = transport.Exchange(code)
		if err != nil {
			return nil, err
		}
	}

	transport.Token = token
	return transport.Client(), nil
}
<|endoftext|>


<commit_before>package oauth

import (
	"crypto/hmac"
	"encoding/base64"
	"fmt"
	"http"
	"io/ioutil"
	"os"
	"rand"
	"sort"
	"strconv"
	"time"
)

const (
	OAUTH_VERSION    = "1.0"
	SIGNATURE_METHOD = "HMAC-SHA1"

	// Request
	CALLBACK_PARAM         = "oauth_callback"
	CONSUMER_KEY_PARAM     = "oauth_consumer_key"
	NONCE_PARAM            = "oauth_nonce"
	SIGNATURE_METHOD_PARAM = "oauth_signature_method"
	SIGNATURE_PARAM        = "oauth_signature"
	TIMESTAMP_PARAM        = "oauth_timestamp"
	VERIFIER_PARAM         = "oauth_verifier"
	VERSION_PARAM          = "oauth_version"

	// Response
	TOKEN_PARAM        = "oauth_token"
	TOKEN_SECRET_PARAM = "oauth_token_secret"
)

type Consumer struct {
	// Get these from the OAuth Service Provider
	ConsumerKey    string
	ConsumerSecret string

	RequestTokenUrl   string
	AuthorizeTokenUrl string
	AccessTokenUrl    string

	CallbackUrl      string
	AdditionalParams map[string]string
}

type UnauthorizedToken struct {
	Token       string
	TokenSecret string
}

type AuthorizedToken struct {
	Token       string
	TokenSecret string
}

type request struct {
  method string
  url string
  oauthParams *OrderedParams
  userParams map[string]string
}

func newGetRequest(url string, oauthParams *OrderedParams) *request {
  return &request{
      method: "GET",
      url: url,
      oauthParams: oauthParams,
  }
}

func (c *Consumer) GetRequestToken() (*UnauthorizedToken, os.Error) {
	params := baseParams(c.ConsumerKey, c.AdditionalParams)
	params.Add(CALLBACK_PARAM, c.CallbackUrl)

  req := newGetRequest(c.RequestTokenUrl, params)
  c.signRequest(req, c.makeKey("")) // We don't have a token secret for the key yet

	resp, err := getBody(c.RequestTokenUrl, params)
	if err != nil {
		return nil, err
	}

	token, secret, err := parseTokenAndSecret(*resp)
	if err != nil {
		return nil, err
	}
	return &UnauthorizedToken{
		Token:       *token,
		TokenSecret: *secret,
	}, nil
}

func (c *Consumer) signRequest(req *request, key string) *request {
  base_string := c.requestString(req.method, req.url, req.oauthParams)
  req.oauthParams.Add(SIGNATURE_PARAM, sign(base_string, key))
  return req
}

func (c *Consumer) TokenAuthorizationUrl(token *UnauthorizedToken) string {
	return c.AuthorizeTokenUrl + "?oauth_token=" + token.Token
}

func (c *Consumer) AuthorizeToken(unauthToken *UnauthorizedToken, verificationCode string) (*AuthorizedToken, os.Error) {
	params := baseParams(c.ConsumerKey, c.AdditionalParams)

	params.Add(VERIFIER_PARAM, verificationCode)
	params.Add(TOKEN_PARAM, unauthToken.Token)

  req := newGetRequest(c.AccessTokenUrl, params)
  c.signRequest(req, c.makeKey(unauthToken.TokenSecret))

	resp, err := getBody(c.AccessTokenUrl, params)

	token, secret, err := parseTokenAndSecret(*resp)
	if err != nil {
		return nil, err
	}
	return &AuthorizedToken{
		Token:       *token,
		TokenSecret: *secret,
	},
		nil
}

func (c *Consumer) Get(url string, userParams map[string]string, token *AuthorizedToken) (*http.Response, os.Error) {
	allParams := baseParams(c.ConsumerKey, c.AdditionalParams)
	authParams := allParams.Clone()

	queryParams := ""
	separator := "?"
	if userParams != nil {
		for key, value := range userParams {
			allParams.Add(key, value)
			queryParams += separator + escape(key) + "=" + escape(value)
			separator = "&"
		}
	}

	allParams.Add(TOKEN_PARAM, token.Token)
	authParams.Add(TOKEN_PARAM, token.Token)

	key := c.makeKey(token.TokenSecret)

	base_string := c.requestString("GET", url, allParams)
	authParams.Add(SIGNATURE_PARAM, sign(base_string, key))

	return get(url+queryParams, authParams)
}

func (c *Consumer) makeKey(tokenSecret string) string {
	return escape(c.ConsumerSecret) + "&" + escape(tokenSecret)
}

func parseTokenAndSecret(data string) (*string, *string, os.Error) {
	parts, err := http.ParseQuery(data)
	if err != nil {
		return nil, nil, err
	}

	if len(parts[TOKEN_PARAM]) < 1 {
		return nil, nil, os.NewError("Missing " + TOKEN_PARAM + " in response.")
	}
	if len(parts[TOKEN_SECRET_PARAM]) < 1 {
		return nil, nil, os.NewError("Missing " + TOKEN_SECRET_PARAM + " in response.")
	}

	return &parts[TOKEN_PARAM][0], &parts[TOKEN_SECRET_PARAM][0], nil
}

func baseParams(consumerKey string, additionalParams map[string]string) *OrderedParams {
	params := NewOrderedParams()
	params.Add(VERSION_PARAM, OAUTH_VERSION)
	params.Add(SIGNATURE_METHOD_PARAM, SIGNATURE_METHOD)
	params.Add(TIMESTAMP_PARAM, strconv.Itoa64(time.Seconds()))
	params.Add(NONCE_PARAM, strconv.Itoa64(rand.Int63()))
	params.Add(CONSUMER_KEY_PARAM, consumerKey)
	for key, value := range additionalParams {
		params.Add(key, value)
	}
	return params
}

func sign(message string, key string) string {
	fmt.Println("Signing:" + message)
	fmt.Println("Key:" + key)
	hashfun := hmac.NewSHA1([]byte(key))
	hashfun.Write([]byte(message))
	rawsignature := hashfun.Sum()
	base64signature := make([]byte, base64.StdEncoding.EncodedLen(len(rawsignature)))
	base64.StdEncoding.Encode(base64signature, rawsignature)
	return string(base64signature)
}

func escape(input string) string {
	return http.URLEscape(input)
}

func (c *Consumer) requestString(method string, url string, params *OrderedParams) string {
	result := method + "&" + escape(url)
	for pos, key := range params.Keys() {
		if pos == 0 {
			result += "&"
		} else {
			result += escape("&")
		}
		result += escape(fmt.Sprintf("%s=%s", key, params.Get(key)))
	}
	return result
}

func getBody(url string, oauthParams *OrderedParams) (*string, os.Error) {
	resp, err := get(url, oauthParams)
	if err != nil {
		return nil, err
	}
	bytes, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return nil, err
	}
	str := string(bytes)
	fmt.Println("BODY RESPONSE: " + str)
	return &str, nil
}

func get(url string, oauthParams *OrderedParams) (*http.Response, os.Error) {
	fmt.Println("GET url: " + url)

	var req http.Request
	req.Method = "GET"
	req.Header = http.Header{}
	parsedurl, err := http.ParseURL(url)
	if err != nil {
		return nil, err
	}
	req.URL = parsedurl

	authhdr := "OAuth "
	for pos, key := range oauthParams.Keys() {
		if pos > 0 {
			authhdr += ",\n    "
		}
		authhdr += key + "=\"" + oauthParams.Get(key) + "\""
	}
	fmt.Println("AUTH-HDR: " + authhdr)
	req.Header.Add("Authorization", authhdr)

	client := &http.Client{}
	return client.Do(&req)
}

//
// ORDERED PARAMS
//

type OrderedParams struct {
	allParams   map[string]string
	keyOrdering []string
}

func NewOrderedParams() *OrderedParams {
	return &OrderedParams{
		allParams:   make(map[string]string),
		keyOrdering: make([]string, 0),
	}
}

func (o *OrderedParams) Get(key string) string {
	return o.allParams[key]
}

func (o *OrderedParams) Keys() []string {
	sort.Sort(o)
	return o.keyOrdering
}

func (o *OrderedParams) Add(key, value string) {
	o.allParams[key] = http.URLEscape(value)
	o.keyOrdering = append(o.keyOrdering, key)
}

func (o *OrderedParams) Len() int {
	return len(o.keyOrdering)
}

func (o *OrderedParams) Less(i int, j int) bool {
	return o.keyOrdering[i] < o.keyOrdering[j]
}

func (o *OrderedParams) Swap(i int, j int) {
	o.keyOrdering[i], o.keyOrdering[j] = o.keyOrdering[j], o.keyOrdering[i]
}

func (o *OrderedParams) Clone() *OrderedParams {
	clone := NewOrderedParams()
	for _, key := range o.Keys() {
		clone.Add(key, o.Get(key))
	}
	return clone
}
<commit_msg>i broke something<commit_after>package oauth

import (
	"crypto/hmac"
	"encoding/base64"
	"fmt"
	"http"
	"io/ioutil"
	"os"
	"rand"
	"sort"
	"strconv"
	"time"
)

const (
	OAUTH_VERSION    = "1.0"
	SIGNATURE_METHOD = "HMAC-SHA1"

	CALLBACK_PARAM         = "oauth_callback"
	CONSUMER_KEY_PARAM     = "oauth_consumer_key"
	NONCE_PARAM            = "oauth_nonce"
	SIGNATURE_METHOD_PARAM = "oauth_signature_method"
	SIGNATURE_PARAM        = "oauth_signature"
	TIMESTAMP_PARAM        = "oauth_timestamp"
	TOKEN_PARAM        = "oauth_token"
	TOKEN_SECRET_PARAM = "oauth_token_secret"
	VERIFIER_PARAM         = "oauth_verifier"
	VERSION_PARAM          = "oauth_version"
)

type Consumer struct {
	// Get these from the OAuth Service Provider
	ConsumerKey    string
	ConsumerSecret string

	RequestTokenUrl   string
	AuthorizeTokenUrl string
	AccessTokenUrl    string

	CallbackUrl      string
	AdditionalParams map[string]string

  httpClient http.Client
}

type UnauthorizedToken struct {
	Token       string
	TokenSecret string
}

type AuthorizedToken struct {
	Token       string
	TokenSecret string
}

type request struct {
  method string
  url string
  oauthParams *OrderedParams
  userParams map[string]string
}

func newGetRequest(url string, oauthParams *OrderedParams) *request {
  return &request{
      method: "GET",
      url: url,
      oauthParams: oauthParams,
  }
}

func (c *Consumer) GetRequestToken() (*UnauthorizedToken, os.Error) {
	params := baseParams(c.ConsumerKey, c.AdditionalParams)
	params.Add(CALLBACK_PARAM, c.CallbackUrl)

  req := newGetRequest(c.RequestTokenUrl, params)
  c.signRequest(req, c.makeKey("")) // We don't have a token secret for the key yet

	resp, err := c.getBody(c.RequestTokenUrl, params)
	if err != nil {
		return nil, err
	}

	token, secret, err := parseTokenAndSecret(*resp)
	if err != nil {
		return nil, err
	}
	return &UnauthorizedToken{
		Token:       *token,
		TokenSecret: *secret,
	}, nil
}

func (c *Consumer) signRequest(req *request, key string) *request {
  base_string := c.requestString(req.method, req.url, req.oauthParams)
  req.oauthParams.Add(SIGNATURE_PARAM, sign(base_string, key))
  return req
}

func (c *Consumer) TokenAuthorizationUrl(token *UnauthorizedToken) string {
	return c.AuthorizeTokenUrl + "?oauth_token=" + token.Token
}

func (c *Consumer) AuthorizeToken(unauthToken *UnauthorizedToken, verificationCode string) (*AuthorizedToken, os.Error) {
	params := baseParams(c.ConsumerKey, c.AdditionalParams)

	params.Add(VERIFIER_PARAM, verificationCode)
	params.Add(TOKEN_PARAM, unauthToken.Token)

  req := newGetRequest(c.AccessTokenUrl, params)
  c.signRequest(req, c.makeKey(unauthToken.TokenSecret))

	resp, err := c.getBody(c.AccessTokenUrl, params)

	token, secret, err := parseTokenAndSecret(*resp)
	if err != nil {
		return nil, err
	}
	return &AuthorizedToken{
		Token:       *token,
		TokenSecret: *secret,
	},
		nil
}

func (c *Consumer) Get(url string, userParams map[string]string, token *AuthorizedToken) (*http.Response, os.Error) {
	allParams := baseParams(c.ConsumerKey, c.AdditionalParams)
	authParams := allParams.Clone()

	queryParams := ""
	separator := "?"
	if userParams != nil {
		for key, value := range userParams {
			allParams.Add(key, value)
			queryParams += separator + escape(key) + "=" + escape(value)
			separator = "&"
		}
	}

	allParams.Add(TOKEN_PARAM, token.Token)
	authParams.Add(TOKEN_PARAM, token.Token)

	key := c.makeKey(token.TokenSecret)

	base_string := c.requestString("GET", url, allParams)
	authParams.Add(SIGNATURE_PARAM, sign(base_string, key))

	return c.get(url+queryParams, authParams)
}

func (c *Consumer) makeKey(tokenSecret string) string {
	return escape(c.ConsumerSecret) + "&" + escape(tokenSecret)
}

func parseTokenAndSecret(data string) (*string, *string, os.Error) {
	parts, err := http.ParseQuery(data)
	if err != nil {
		return nil, nil, err
	}

	if len(parts[TOKEN_PARAM]) < 1 {
		return nil, nil, os.NewError("Missing " + TOKEN_PARAM + " in response.")
	}
	if len(parts[TOKEN_SECRET_PARAM]) < 1 {
		return nil, nil, os.NewError("Missing " + TOKEN_SECRET_PARAM + " in response.")
	}

	return &parts[TOKEN_PARAM][0], &parts[TOKEN_SECRET_PARAM][0], nil
}

func baseParams(consumerKey string, additionalParams map[string]string) *OrderedParams {
	params := NewOrderedParams()
  r := rand.New(rand.NewSource(time.Seconds()))
	params.Add(VERSION_PARAM, OAUTH_VERSION)
	params.Add(SIGNATURE_METHOD_PARAM, SIGNATURE_METHOD)
	params.Add(TIMESTAMP_PARAM, strconv.Itoa64(time.Seconds()))
	params.Add(NONCE_PARAM, strconv.Itoa64(r.Int63()))
	params.Add(CONSUMER_KEY_PARAM, consumerKey)
	for key, value := range additionalParams {
		params.Add(key, value)
	}
	return params
}

func sign(message string, key string) string {
	fmt.Println("Signing:" + message)
	fmt.Println("Key:" + key)
	hashfun := hmac.NewSHA1([]byte(key))
	hashfun.Write([]byte(message))
	rawsignature := hashfun.Sum()
	base64signature := make([]byte, base64.StdEncoding.EncodedLen(len(rawsignature)))
	base64.StdEncoding.Encode(base64signature, rawsignature)
	return string(base64signature)
}

func escape(input string) string {
	return http.URLEscape(input)
}

func (c *Consumer) requestString(method string, url string, params *OrderedParams) string {
	result := method + "&" + escape(url)
	for pos, key := range params.Keys() {
		if pos == 0 {
			result += "&"
		} else {
			result += escape("&")
		}
		result += escape(fmt.Sprintf("%s=%s", key, params.Get(key)))
	}
	return result
}

func (c *Consumer) getBody(url string, oauthParams *OrderedParams) (*string, os.Error) {
	resp, err := c.get(url, oauthParams)
	if err != nil {
		return nil, err
	}
	bytes, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return nil, err
	}
	str := string(bytes)
	fmt.Println("BODY RESPONSE: " + str)
	return &str, nil
}

func (c *Consumer) get(url string, oauthParams *OrderedParams) (*http.Response, os.Error) {
	fmt.Println("GET url: " + url)

	var req http.Request
	req.Method = "GET"
	req.Header = http.Header{}
	parsedurl, err := http.ParseURL(url)
	if err != nil {
		return nil, err
	}
	req.URL = parsedurl

	authhdr := "OAuth "
	for pos, key := range oauthParams.Keys() {
		if pos > 0 {
			authhdr += ",\n    "
		}
		authhdr += key + "=\"" + oauthParams.Get(key) + "\""
	}
	fmt.Println("AUTH-HDR: " + authhdr)
	req.Header.Add("Authorization", authhdr)

	return c.httpClient.Do(&req)
}

//
// ORDERED PARAMS
//

type OrderedParams struct {
	allParams   map[string]string
	keyOrdering []string
}

func NewOrderedParams() *OrderedParams {
	return &OrderedParams{
		allParams:   make(map[string]string),
		keyOrdering: make([]string, 0),
	}
}

func (o *OrderedParams) Get(key string) string {
	return o.allParams[key]
}

func (o *OrderedParams) Keys() []string {
	sort.Sort(o)
	return o.keyOrdering
}

func (o *OrderedParams) Add(key, value string) {
	o.allParams[key] = http.URLEscape(value)
	o.keyOrdering = append(o.keyOrdering, key)
}

func (o *OrderedParams) Len() int {
	return len(o.keyOrdering)
}

func (o *OrderedParams) Less(i int, j int) bool {
	return o.keyOrdering[i] < o.keyOrdering[j]
}

func (o *OrderedParams) Swap(i int, j int) {
	o.keyOrdering[i], o.keyOrdering[j] = o.keyOrdering[j], o.keyOrdering[i]
}

func (o *OrderedParams) Clone() *OrderedParams {
	clone := NewOrderedParams()
	for _, key := range o.Keys() {
		clone.Add(key, o.Get(key))
	}
	return clone
}
<|endoftext|>


<commit_before>package hamming

import (
	"testing"
)

var testCases = []struct {
	expected         int
	strandA, strandB string
	description      string
}{
	{0, "", "", "no difference between empty strands"},
	{2, "AG", "CT", "complete hamming distance for small strand"},
	{0, "A", "A", "no difference between identical strands"},
	{1, "A", "G", "complete distance for single nucleotide strand"},
	{1, "AT", "CT", "small hamming distance"},
	{1, "GGACG", "GGTCG", "small hamming distance in longer strand"},
	{0, "AAAG", "AAA", "ignores extra length on first strand when longer"},
	{0, "AAA", "AAAG", "ignores extra length on second strand when longer"},
	{4, "GATACA", "GCATAA", "large hamming distance"},
	{9, "GGACGGATTCTG", "AGGACGGATTCT", "hamming distance in very long strand"},
}

func TestHamming(t *testing.T) {
	for _, tc := range testCases {

		observed := Distance(tc.strandA, tc.strandB)

		if tc.expected != observed {
			t.Fatalf(`%s:
expected: %v
observed: %v`,
				tc.description,
				tc.expected,
				observed,
			)
		}
	}
}
<commit_msg>Tweak hamming test suite in Go<commit_after>package hamming

import (
	"testing"
)

var testCases = []struct {
	expected         int
	strandA, strandB string
	description      string
}{
	{0, "", "", "no difference between empty strands"},
	{2, "AG", "CT", "complete hamming distance for small strands"},
	{0, "A", "A", "no difference between identical strands"},
	{1, "A", "G", "complete distance for single nucleotide strands"},
	{1, "AT", "CT", "small hamming distance"},
	{1, "GGACG", "GGTCG", "small hamming distance in longer strands"},
	{0, "AAAG", "AAA", "ignores extra length on first strand when longer"},
	{0, "AAA", "AAAG", "ignores extra length on second strand when longer"},
	{4, "GATACA", "GCATAA", "large hamming distance"},
	{9, "GGACGGATTCTG", "AGGACGGATTCT", "hamming distance in very long strands"},
}

func TestHamming(t *testing.T) {
	for _, tc := range testCases {

		observed := Distance(tc.strandA, tc.strandB)

		if tc.expected != observed {
			t.Fatalf(`%s:
{%v,%v}
expected: %v
observed: %v`,
				tc.description,
				tc.strandA,
				tc.strandB,
				tc.expected,
				observed,
			)
		}
	}
}
<|endoftext|>