<commit_before>package rescuetime

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"

	simplejson "github.com/bitly/go-simplejson"
)

const (
	dataURL         string = "https://www.rescuetime.com/anapi/data"
	dailySummaryURL string = "https://www.rescuetime.com/anapi/daily_summary_feed"
)

type RescueTime struct {
	ApiKey string
}

type RescueTimeDailySummary struct {
	AllDistractingDurationFormatted             string  `json:"all_distracting_duration_formatted"`
	AllDistractingHours                         float64 `json:"all_distracting_hours"`
	AllDistractingPercentage                    float64 `json:"all_distracting_percentage"`
	AllProductiveDurationFormatted              string  `json:"all_productive_duration_formatted"`
	AllProductiveHours                          float64 `json:"all_productive_hours"`
	AllProductivePercentage                     float64 `json:"all_productive_percentage"`
	BusinessDurationFormatted                   string  `json:"business_duration_formatted"`
	BusinessHours                               float64 `json:"business_hours"`
	BusinessPercentage                          float64 `json:"business_percentage"`
	CommunicationAndSchedulingDurationFormatted string  `json:"communication_and_scheduling_duration_formatted"`
	CommunicationAndSchedulingHours             float64 `json:"communication_and_scheduling_hours"`
	CommunicationAndSchedulingPercentage        float64 `json:"communication_and_scheduling_percentage"`
	Date                                        string  `json:"date"`
	DesignAndCompositionDurationFormatted       string  `json:"design_and_composition_duration_formatted"`
	DesignAndCompositionHours                   float64 `json:"design_and_composition_hours"`
	DesignAndCompositionPercentage              float64 `json:"design_and_composition_percentage"`
	DistractingDurationFormatted                string  `json:"distracting_duration_formatted"`
	DistractingHours                            float64 `json:"distracting_hours"`
	DistractingPercentage                       float64 `json:"distracting_percentage"`
	EntertainmentDurationFormatted              string  `json:"entertainment_duration_formatted"`
	EntertainmentHours                          float64 `json:"entertainment_hours"`
	EntertainmentPercentage                     float64 `json:"entertainment_percentage"`
	ID                                          float64 `json:"id"`
	NeutralDurationFormatted                    string  `json:"neutral_duration_formatted"`
	NeutralHours                                float64 `json:"neutral_hours"`
	NeutralPercentage                           float64 `json:"neutral_percentage"`
	NewsDurationFormatted                       string  `json:"news_duration_formatted"`
	NewsHours                                   float64 `json:"news_hours"`
	NewsPercentage                              float64 `json:"news_percentage"`
	ProductiveDurationFormatted                 string  `json:"productive_duration_formatted"`
	ProductiveHours                             float64 `json:"productive_hours"`
	ProductivePercentage                        float64 `json:"productive_percentage"`
	ProductivityPulse                           float64 `json:"productivity_pulse"`
	ReferenceAndLearningDurationFormatted       string  `json:"reference_and_learning_duration_formatted"`
	ReferenceAndLearningHours                   float64 `json:"reference_and_learning_hours"`
	ReferenceAndLearningPercentage              float64 `json:"reference_and_learning_percentage"`
	ShoppingDurationFormatted                   string  `json:"shopping_duration_formatted"`
	ShoppingHours                               float64 `json:"shopping_hours"`
	ShoppingPercentage                          float64 `json:"shopping_percentage"`
	SocialNetworkingDurationFormatted           string  `json:"social_networking_duration_formatted"`
	SocialNetworkingHours                       float64 `json:"social_networking_hours"`
	SocialNetworkingPercentage                  float64 `json:"social_networking_percentage"`
	SoftwareDevelopmentDurationFormatted        string  `json:"software_development_duration_formatted"`
	SoftwareDevelopmentHours                    float64 `json:"software_development_hours"`
	SoftwareDevelopmentPercentage               float64 `json:"software_development_percentage"`
	TotalDurationFormatted                      string  `json:"total_duration_formatted"`
	TotalHours                                  float64 `json:"total_hours"`
	UncategorizedDurationFormatted              string  `json:"uncategorized_duration_formatted"`
	UncategorizedHours                          float64 `json:"uncategorized_hours"`
	UncategorizedPercentage                     float64 `json:"uncategorized_percentage"`
	UtilitiesDurationFormatted                  string  `json:"utilities_duration_formatted"`
	UtilitiesHours                              float64 `json:"utilities_hours"`
	UtilitiesPercentage                         float64 `json:"utilities_percentage"`
	VeryDistractingDurationFormatted            string  `json:"very_distracting_duration_formatted"`
	VeryDistractingHours                        float64 `json:"very_distracting_hours"`
	VeryDistractingPercentage                   float64 `json:"very_distracting_percentage"`
	VeryProductiveDurationFormatted             string  `json:"very_productive_duration_formatted"`
	VeryProductiveHours                         float64 `json:"very_productive_hours"`
	VeryProductivePercentage                    float64 `json:"very_productive_percentage"`
}

type RescueTimeData struct {
	Notes      string               `json:"notes"`
	RowHeaders []string             `json:"row_headers"`
	Rows       []MiniRescueTimeData `json:"rows"`
}

type MiniRescueTimeData struct {
	Date           time.Time     `json:"date,omitempty"`
	Rank           int           `json:"rank,omitempty"`
	TimeSpent      time.Duration `json:"time_spent,omitempty"`
	NumberOfPeople int           `json:"number_of_people"`
	Activity       string        `json:"activity"`
	Category       string        `json:"category"`
	Productivity   int           `json:"productivity"`
}

func (r *RescueTime) buildUrl(baseUrl string, arguments ...[]string) (string, error) {
	parsedURL, err := url.Parse(baseUrl)
	if err != nil {
		return "", err
	}
	query := parsedURL.Query()
	for _, argPair := range arguments {
		query.Add(argPair[0], argPair[1])
	}
	query.Set("key", r.ApiKey)
	query.Set("format", "json")
	parsedURL.RawQuery = query.Encode()
	return parsedURL.String(), nil
}

func (r *RescueTime) GetResponse(getUrl string) ([]byte, error) {
	if r.ApiKey == "" {
		return nil, errors.New("Please provide API key")
	}
	response, err := http.Get(getUrl)
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()
	contents, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}
	return contents, nil
}

func (r *RescueTime) GetData(timezone string, arguments ...[]string) (RescueTimeData, error) {
	var rtd RescueTimeData

	builtUrl, err := r.buildUrl(dataURL, arguments...)
	if err != nil {
		return rtd, err
	}

	contents, err := r.GetResponse(builtUrl)
	if err != nil {
		return rtd, err
	}
	currentJSON := simplejson.New()
	currentJSON.UnmarshalJSON(contents)

	data := RescueTimeData{}

	var notes string
	notes = fmt.Sprintf("%s", (*currentJSON.Get("notes")).MustString())
	data.Notes = notes

	var rowHeaders []string
	rowHeaders, _ = (*currentJSON.Get("row_headers")).StringArray()
	data.RowHeaders = rowHeaders

	var toAppend []MiniRescueTimeData
	for i := 0; i < 36; i++ {
		rows := (*currentJSON.Get("rows")).GetIndex(i)
		current := MiniRescueTimeData{}
		if rowHeaders[0] == "Date" {
			date, _ := (*rows).GetIndex(0).String()
			parsed, err := time.Parse("2006-01-02T15:04:05", date)
			if timezone != "" {
				location, err := time.LoadLocation(timezone)
				if err != nil {
					return rtd, err
				}
				parsed = parsed.In(location)
			}
			if err != nil {
				return rtd, err
			}
			current.Date = parsed
		}
		if rowHeaders[0] == "Rank" {
			current.Rank, _ = (*rows).GetIndex(0).Int()
		}
		timeSpent, _ := (*rows).GetIndex(1).Int()
		timeDuration := time.Duration(timeSpent) * time.Second
		current.TimeSpent = timeDuration
		current.NumberOfPeople, _ = (*rows).GetIndex(2).Int()
		current.Activity, _ = (*rows).GetIndex(3).String()
		current.Category, _ = (*rows).GetIndex(4).String()
		current.Productivity, _ = (*rows).GetIndex(5).Int()
		toAppend = append(toAppend, current)
	}
	data.Rows = toAppend
	return data, nil
}

func (r *RescueTime) DailySummary(arguments ...[]string) ([]RescueTimeDailySummary, error) {
	var summaries []RescueTimeDailySummary
	builtUrl, err := r.buildUrl(dailySummaryURL, arguments...)
	if err != nil {
		return summaries, err
	}
	contents, err := r.GetResponse(builtUrl)
	if err != nil {
		return summaries, err
	}
	keys := make([]RescueTimeDailySummary, 0)
	err = json.Unmarshal(contents, &keys)
	if err != nil {
		return summaries, err
	}
	return keys, nil
}
<commit_msg>Dynamically build rows instead of hardcoding every field type<commit_after>package rescuetime

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"strings"
	"time"

	simplejson "github.com/bitly/go-simplejson"
)

const (
	dataURL         string = "https://www.rescuetime.com/anapi/data"
	dailySummaryURL string = "https://www.rescuetime.com/anapi/daily_summary_feed"
)

type RescueTime struct {
	ApiKey string
}

type RescueTimeDailySummary struct {
	AllDistractingDurationFormatted             string  `json:"all_distracting_duration_formatted"`
	AllDistractingHours                         float64 `json:"all_distracting_hours"`
	AllDistractingPercentage                    float64 `json:"all_distracting_percentage"`
	AllProductiveDurationFormatted              string  `json:"all_productive_duration_formatted"`
	AllProductiveHours                          float64 `json:"all_productive_hours"`
	AllProductivePercentage                     float64 `json:"all_productive_percentage"`
	BusinessDurationFormatted                   string  `json:"business_duration_formatted"`
	BusinessHours                               float64 `json:"business_hours"`
	BusinessPercentage                          float64 `json:"business_percentage"`
	CommunicationAndSchedulingDurationFormatted string  `json:"communication_and_scheduling_duration_formatted"`
	CommunicationAndSchedulingHours             float64 `json:"communication_and_scheduling_hours"`
	CommunicationAndSchedulingPercentage        float64 `json:"communication_and_scheduling_percentage"`
	Date                                        string  `json:"date"`
	DesignAndCompositionDurationFormatted       string  `json:"design_and_composition_duration_formatted"`
	DesignAndCompositionHours                   float64 `json:"design_and_composition_hours"`
	DesignAndCompositionPercentage              float64 `json:"design_and_composition_percentage"`
	DistractingDurationFormatted                string  `json:"distracting_duration_formatted"`
	DistractingHours                            float64 `json:"distracting_hours"`
	DistractingPercentage                       float64 `json:"distracting_percentage"`
	EntertainmentDurationFormatted              string  `json:"entertainment_duration_formatted"`
	EntertainmentHours                          float64 `json:"entertainment_hours"`
	EntertainmentPercentage                     float64 `json:"entertainment_percentage"`
	ID                                          float64 `json:"id"`
	NeutralDurationFormatted                    string  `json:"neutral_duration_formatted"`
	NeutralHours                                float64 `json:"neutral_hours"`
	NeutralPercentage                           float64 `json:"neutral_percentage"`
	NewsDurationFormatted                       string  `json:"news_duration_formatted"`
	NewsHours                                   float64 `json:"news_hours"`
	NewsPercentage                              float64 `json:"news_percentage"`
	ProductiveDurationFormatted                 string  `json:"productive_duration_formatted"`
	ProductiveHours                             float64 `json:"productive_hours"`
	ProductivePercentage                        float64 `json:"productive_percentage"`
	ProductivityPulse                           float64 `json:"productivity_pulse"`
	ReferenceAndLearningDurationFormatted       string  `json:"reference_and_learning_duration_formatted"`
	ReferenceAndLearningHours                   float64 `json:"reference_and_learning_hours"`
	ReferenceAndLearningPercentage              float64 `json:"reference_and_learning_percentage"`
	ShoppingDurationFormatted                   string  `json:"shopping_duration_formatted"`
	ShoppingHours                               float64 `json:"shopping_hours"`
	ShoppingPercentage                          float64 `json:"shopping_percentage"`
	SocialNetworkingDurationFormatted           string  `json:"social_networking_duration_formatted"`
	SocialNetworkingHours                       float64 `json:"social_networking_hours"`
	SocialNetworkingPercentage                  float64 `json:"social_networking_percentage"`
	SoftwareDevelopmentDurationFormatted        string  `json:"software_development_duration_formatted"`
	SoftwareDevelopmentHours                    float64 `json:"software_development_hours"`
	SoftwareDevelopmentPercentage               float64 `json:"software_development_percentage"`
	TotalDurationFormatted                      string  `json:"total_duration_formatted"`
	TotalHours                                  float64 `json:"total_hours"`
	UncategorizedDurationFormatted              string  `json:"uncategorized_duration_formatted"`
	UncategorizedHours                          float64 `json:"uncategorized_hours"`
	UncategorizedPercentage                     float64 `json:"uncategorized_percentage"`
	UtilitiesDurationFormatted                  string  `json:"utilities_duration_formatted"`
	UtilitiesHours                              float64 `json:"utilities_hours"`
	UtilitiesPercentage                         float64 `json:"utilities_percentage"`
	VeryDistractingDurationFormatted            string  `json:"very_distracting_duration_formatted"`
	VeryDistractingHours                        float64 `json:"very_distracting_hours"`
	VeryDistractingPercentage                   float64 `json:"very_distracting_percentage"`
	VeryProductiveDurationFormatted             string  `json:"very_productive_duration_formatted"`
	VeryProductiveHours                         float64 `json:"very_productive_hours"`
	VeryProductivePercentage                    float64 `json:"very_productive_percentage"`
}

type RescueTimeData struct {
	Notes      string               `json:"notes"`
	RowHeaders []string             `json:"row_headers"`
	Rows       []MiniRescueTimeData `json:"rows"`
}

type MiniRescueTimeData struct {
	Date           *time.Time `json:"date,omitempty"`
	Rank           *int       `json:"rank,omitempty"`
	TimeSpent      *int       `json:"timeSpentSeconds,omitempty"`
	NumberOfPeople *int       `json:"numberOfPeople,omitempty"`
	Person         *string    `json:"person,omitempty"`
	Activity       *string    `json:"activity,omitempty"`
	Category       *string    `json:"category,omitempty"`
	Productivity   *int       `json:"productivity,omitempty"`
}

func (r *RescueTime) buildUrl(baseUrl string, arguments ...[]string) (string, error) {
	parsedURL, err := url.Parse(baseUrl)
	if err != nil {
		return "", err
	}
	query := parsedURL.Query()
	for _, argPair := range arguments {
		query.Add(argPair[0], argPair[1])
	}
	query.Set("key", r.ApiKey)
	query.Set("format", "json")
	parsedURL.RawQuery = query.Encode()
	return parsedURL.String(), nil
}

var camelingRegex = regexp.MustCompile("[0-9A-Za-z]+")

func camelCase(src string) string {
	byteSrc := []byte(src)
	chunks := camelingRegex.FindAll(byteSrc, -1)
	for idx, val := range chunks {
		if idx > 0 {
			chunks[idx] = bytes.Title(val)
		}
	}
	return string(bytes.Join(chunks, nil))
}

func (r *RescueTime) GetResponse(getUrl string) ([]byte, error) {
	if r.ApiKey == "" {
		return nil, errors.New("Please provide API key")
	}
	response, err := http.Get(getUrl)
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()
	contents, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}
	return contents, nil
}

func (r *RescueTime) GetData(timezone string, arguments ...[]string) (RescueTimeData, error) {
	var rtd RescueTimeData

	builtUrl, err := r.buildUrl(dataURL, arguments...)
	if err != nil {
		return rtd, err
	}

	contents, err := r.GetResponse(builtUrl)
	if err != nil {
		return rtd, err
	}
	currentJSON, err := simplejson.NewJson(contents)
	if err != nil {
		return rtd, err
	}

	data := RescueTimeData{}

	var notes string
	notes = fmt.Sprintf("%s", currentJSON.Get("notes").MustString())
	data.Notes = notes

	var rowHeaders []string
	headersMap := make(map[int]string)
	for i, s := range currentJSON.Get("row_headers").MustStringArray() {
		rowHeaders = append(rowHeaders, s)
		headersMap[i] = camelCase(strings.ToLower(s))
	}
	data.RowHeaders = rowHeaders

	var toAppend []MiniRescueTimeData
	for _, entry := range currentJSON.Get("rows").MustArray() {
		out := simplejson.New()
		var entryData MiniRescueTimeData
		for k, v := range entry.([]interface{}) {
			switch headersMap[k] {
			case "date":
				parsed, err := time.Parse("2006-01-02T15:04:05", v.(string))
				if err != nil {
					return rtd, err
				}
				if timezone != "" {
					location, err := time.LoadLocation(timezone)
					if err != nil {
						return rtd, err
					}
					parsed = parsed.In(location)
				}
				v = parsed.Format(time.RFC3339)
			}
			out.Set(headersMap[k], v)
		}
		encoded, err := out.Encode()
		if err != nil {
			return rtd, err
		}
		err = json.Unmarshal(encoded, &entryData)
		if err != nil {
			return rtd, err
		}
		toAppend = append(toAppend, entryData)
	}
	data.Rows = toAppend
	return data, nil
}

func (r *RescueTime) DailySummary(arguments ...[]string) ([]RescueTimeDailySummary, error) {
	var summaries []RescueTimeDailySummary
	builtUrl, err := r.buildUrl(dailySummaryURL, arguments...)
	if err != nil {
		return summaries, err
	}
	contents, err := r.GetResponse(builtUrl)
	if err != nil {
		return summaries, err
	}
	keys := make([]RescueTimeDailySummary, 0)
	err = json.Unmarshal(contents, &keys)
	if err != nil {
		return summaries, err
	}
	return keys, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Reed O'Brien <reed@reedobrien.com>.
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

package main

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base32"
	"encoding/binary"
	"encoding/hex"
	"flag"
	"fmt"
	"math"
	"os"
	"strconv"
	"strings"
	"time"
)

const TIME_FMT = "2006-01-02 15:04:05 MST"

var (
	epochSec int64 // Representation of TOTP epoch in seconds
	err      error
	key      []byte // The decoded secret
	incr     func() // function to vary incrementing with window on hotp/totp
	secret   string // The hex or b32 secret
	otp      string // If an OTP is supplied for verification
	nowSec   int64  // Representation of "now" in seconds

	// common flags are added in addFlags
	b32, verbose   *bool
	digits, window *int

	// cFlags is only here for usage()
	cFlag = flag.NewFlagSet("common", flag.ContinueOnError)

	// Flags
	hFlag   = flag.NewFlagSet("hotp", flag.ContinueOnError)
	counter = hFlag.Int64("c", 0, "HOTP counter Value")

	tFlag = flag.NewFlagSet("totp", flag.ContinueOnError)
	now   = tFlag.String("N", "", "Use this time as current time for TOTP")
	step  = tFlag.Int64("s", 30, "The time-step duration")
	epoch = tFlag.String("S", "1970-01-01 00:00:00 UTC", "When to start counting time-steps for TOTP")

	// Need a usage function since we don't build all flag sets unless the
	// program is called correctly.
	usage = func() {
		fmt.Fprintf(os.Stderr, "usage of %s:\n", os.Args[0])
		fmt.Fprintf(os.Stderr,
			"\t%s [hotp|totp] <options> SECRET <OTP>\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "  SECRET is the hex or b32 encoded secret as a string\n")
		fmt.Fprintf(os.Stderr, "  OTP is a one-time password to validate.\n")
		fmt.Fprintf(os.Stderr, "Common options:\n")
		addFlags(cFlag)
		cFlag.PrintDefaults()
		fmt.Fprintf(os.Stderr, "hotp options:\n")
		hFlag.PrintDefaults()
		fmt.Fprintf(os.Stderr, "totp options:\n")
		tFlag.PrintDefaults()
		os.Exit(1)
	}
)

func main() {
	var (
		passcode string
		generate func() (string, error)
	)

	if len(os.Args) < 2 {
		usage()
	}
	switch os.Args[1] {
	case "hotp":
		parseFlags(hFlag)
		generate = genHOTP
		incr = func() {
			*counter++
		}
		if *verbose {
			fmt.Println("Parsed hotp flags.")
			if *b32 {
				fmt.Println("Base 32 secret", secret)

			}
			if !*b32 {
				fmt.Println("Hex secret:", secret)
			}
			if otp != "" {
				fmt.Println("OTP:", otp)
			}
			fmt.Println("Digits:", *digits)
			fmt.Println("Window size:", *window)
			fmt.Println("Start Counter:", *counter)
		}
		if len(otp) > 0 {
			valid := validateHOTP()
			if valid {
				if *verbose {
					fmt.Println()
				}
				fmt.Println(*counter)
				os.Exit(0)
			}
			if !valid {
				fmt.Fprintf(os.Stderr, "%s: validating one time password failed (-2)\n", os.Args[0])
				// oathtool exits with this code it seems.
				os.Exit(1)
			}
		}
	case "totp":
		parseFlags(tFlag)
		generate = genTOTP
		if *now == "" {
			nowSec = time.Now().UTC().Unix()
			*now = time.Unix(nowSec, 0).Format(TIME_FMT)
		} else {
			nowT, err := time.Parse(TIME_FMT, *now)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Failed to parse now (-N): %s", err)
			}
			nowSec = nowT.UTC().Unix()

		}
		if *verbose {
			fmt.Println("Parsed totp flags.")
			if *b32 {
				fmt.Println("Base 32 secret", secret)

			}
			if !*b32 {
				fmt.Println("Hex secret:", secret)
			}
			if otp != "" {
				fmt.Println("OTP:", otp)
			}
			fmt.Println("Digits:", *digits)
			fmt.Println("Window size:", *window)
			fmt.Println("Step size (seconds):", *step)
			fmt.Println("Start time:", *epoch)
			fmt.Println("Current time:", *now)
			fmt.Println("Counter:", nowSec / *step)

		}
		epochT, err := time.Parse(TIME_FMT, *epoch)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to parse start time (-S): %s\n", err)
		}
		epochSec = epochT.UTC().Unix()
		incr = func() {
			nowSec += *step
		}

	default:
		usage()
	}

	key, err = getKey()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error decoding secret: %s", err)
		os.Exit(1)
	}

	var max int64

	if *window > 0 {
		max = *counter + int64(*window)
	}
	if *window == 0 {
		max = 0
	}
	if *verbose {
		fmt.Println()
	}
	for i := int64(0); i <= max; i++ {
		passcode, err = generate()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to generate passcode: %s:\n", err)
		}
		fmt.Println(passcode)
		incr()
	}
}

//// OTP functions

func genHOTP() (string, error) {
	var code uint32

	hash := hmac.New(sha1.New, key)

	err = binary.Write(hash, binary.BigEndian, *counter)
	if err != nil {
		return "", err
	}

	h := hash.Sum(nil)
	offset := h[19] & 0x0f

	trunc := binary.BigEndian.Uint32(h[offset : offset+4])
	trunc &= 0x7fffffff
	code = trunc % uint32(math.Pow(10, float64(*digits)))
	passcodeFormat := "%0" + strconv.Itoa(*digits) + "d"

	return fmt.Sprintf(passcodeFormat, code), nil
}

func genTOTP() (string, error) {
	var code string
	*counter = nowSec / *step
	code, err = genHOTP()
	return code, err
}

func validateHOTP() bool {
	if len(otp) != *digits {
		return false
	}
	key, err = getKey()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error decoding secret: %s", err)
		os.Exit(1)
	}
	for i := 0; i < *window; i++ {
		code, _ := genHOTP()
		if code == otp {
			return true
		}
		*counter++
	}
	return false
}

//// Helpers

func getKey() ([]byte, error) {
	var (
		key []byte
	)

	if *b32 {
		key, err = base32.StdEncoding.DecodeString(secret)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Err decoding secret: %s\n", err)
			return key, err
		}
	}
	if !*b32 {
		key, err = hex.DecodeString(secret)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Err decoding secret: %s\n", err)
			return key, err
		}
	}
	return key, nil
}

//// arg parsing functions

func addFlags(f *flag.FlagSet) {
	// common flags add in flagParse method
	b32 = f.Bool("b", false, "Use b32 encoding instead of hex")
	digits = f.Int("d", 6, "The number of digits in the OTP")
	window = f.Int("w", 0, "Window of counter values to test when validating OTPs")
	verbose = f.Bool("v", false, "Explain what is being done.")
}

func getPositionalArgs(f *flag.FlagSet) {
	secret = strings.ToUpper(f.Arg(0))
	secret = strings.Replace(secret, " ", "", -1)
	if secret == "" {
		usage()
	}

	if *b32 {
		// repad base 32 strings if they are short.
		for len(secret) < 32 {
			secret = secret + "="
		}
	}

	otp = f.Arg(1)
}

func parseFlags(f *flag.FlagSet) {
	addFlags(f)
	err = f.Parse(os.Args[2:])
	if err != nil {
		os.Exit(1)
	}
	getPositionalArgs(f)
}
<commit_msg>Implement TOTP validation.<commit_after>// Copyright 2015 Reed O'Brien <reed@reedobrien.com>.
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

package main

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base32"
	"encoding/binary"
	"encoding/hex"
	"flag"
	"fmt"
	"math"
	"os"
	"strconv"
	"strings"
	"time"
)

const TIME_FMT = "2006-01-02 15:04:05 MST"

var (
	epochSec int64 // Representation of TOTP epoch in seconds
	err      error
	key      []byte // The decoded secret
	incr     func() // function to vary incrementing with window on hotp/totp
	secret   string // The hex or b32 secret
	otp      string // If an OTP is supplied for verification
	nowSec   int64  // Representation of "now" in seconds

	// common flags are added in addFlags
	b32, verbose   *bool
	digits, window *int

	// cFlags is only here for usage()
	cFlag = flag.NewFlagSet("common", flag.ContinueOnError)

	// Flags
	hFlag   = flag.NewFlagSet("hotp", flag.ContinueOnError)
	counter = hFlag.Int64("c", 0, "HOTP counter Value")

	tFlag = flag.NewFlagSet("totp", flag.ContinueOnError)
	now   = tFlag.String("N", "", "Use this time as current time for TOTP")
	step  = tFlag.Int64("s", 30, "The time-step duration")
	epoch = tFlag.String("S", "1970-01-01 00:00:00 UTC", "When to start counting time-steps for TOTP")

	// Need a usage function since we don't build all flag sets unless the
	// program is called correctly.
	usage = func() {
		fmt.Fprintf(os.Stderr, "usage of %s:\n", os.Args[0])
		fmt.Fprintf(os.Stderr,
			"\t%s [hotp|totp] <options> SECRET <OTP>\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "  SECRET is the hex or b32 encoded secret as a string\n")
		fmt.Fprintf(os.Stderr, "  OTP is a one-time password to validate.\n")
		fmt.Fprintf(os.Stderr, "Common options:\n")
		addFlags(cFlag)
		cFlag.PrintDefaults()
		fmt.Fprintf(os.Stderr, "hotp options:\n")
		hFlag.PrintDefaults()
		fmt.Fprintf(os.Stderr, "totp options:\n")
		tFlag.PrintDefaults()
		os.Exit(1)
	}
)

func main() {
	var (
		passcode string
		generate func() (string, error)
	)

	if len(os.Args) < 2 {
		usage()
	}
	switch os.Args[1] {
	case "hotp":
		parseFlags(hFlag)
		generate = genHOTP
		incr = func() {
			*counter++
		}
		if *verbose {
			fmt.Println("Parsed hotp flags.")
			if *b32 {
				fmt.Println("Base 32 secret", secret)

			}
			if !*b32 {
				fmt.Println("Hex secret:", secret)
			}
			if otp != "" {
				fmt.Println("OTP:", otp)
			}
			fmt.Println("Digits:", *digits)
			fmt.Println("Window size:", *window)
			fmt.Println("Start Counter:", *counter)
		}
		if len(otp) > 0 {
			valid := validateHOTP()
			if valid {
				if *verbose {
					fmt.Println()
				}
				fmt.Println(*counter)
				os.Exit(0)
			}
			if !valid {
				fmt.Fprintf(os.Stderr, "%s: validating one time password failed (-2)\n", os.Args[0])
				// oathtool exits with this code it seems.
				os.Exit(1)
			}
		}
	case "totp":
		parseFlags(tFlag)
		generate = genTOTP
		if *now == "" {
			nowSec = time.Now().UTC().Unix()
			*now = time.Unix(nowSec, 0).Format(TIME_FMT)
		} else {
			nowT, err := time.Parse(TIME_FMT, *now)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Failed to parse now (-N): %s", err)
			}
			nowSec = nowT.UTC().Unix()

		}
		if *verbose {
			fmt.Println("Parsed totp flags.")
			if *b32 {
				fmt.Println("Base 32 secret", secret)

			}
			if !*b32 {
				fmt.Println("Hex secret:", secret)
			}
			if otp != "" {
				fmt.Println("OTP:", otp)
			}
			fmt.Println("Digits:", *digits)
			fmt.Println("Window size:", *window)
			fmt.Println("Step size (seconds):", *step)
			fmt.Println("Start time:", *epoch)
			fmt.Println("Current time:", *now)
			fmt.Println("Counter:", nowSec / *step)

		}
		epochT, err := time.Parse(TIME_FMT, *epoch)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to parse start time (-S): %s\n", err)
		}
		epochSec = epochT.UTC().Unix()
		incr = func() {
			nowSec += *step
		}
		if len(otp) > 0 {
			valid := validateTOTP()
			if valid {
				if *verbose {
					fmt.Println()
				}
				fmt.Println(*counter)
				os.Exit(0)
			}
			if !valid {
				fmt.Fprintf(os.Stderr, "%s: validating one time password failed (-2)\n", os.Args[0])
				// oathtool exits with this code it seems.
				os.Exit(1)
			}
		}

	default:
		usage()
	}

	key, err = getKey()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error decoding secret: %s", err)
		os.Exit(1)
	}

	var max int64

	if *window > 0 {
		max = *counter + int64(*window)
	}
	if *window == 0 {
		max = 0
	}
	if *verbose {
		fmt.Println()
	}
	for i := int64(0); i <= max; i++ {
		passcode, err = generate()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to generate passcode: %s:\n", err)
		}
		fmt.Println(passcode)
		incr()
	}
}

//// OTP functions

func genHOTP() (string, error) {
	var code uint32

	hash := hmac.New(sha1.New, key)

	err = binary.Write(hash, binary.BigEndian, *counter)
	if err != nil {
		return "", err
	}

	h := hash.Sum(nil)
	offset := h[19] & 0x0f

	trunc := binary.BigEndian.Uint32(h[offset : offset+4])
	trunc &= 0x7fffffff
	code = trunc % uint32(math.Pow(10, float64(*digits)))
	passcodeFormat := "%0" + strconv.Itoa(*digits) + "d"

	return fmt.Sprintf(passcodeFormat, code), nil
}

func genTOTP() (string, error) {
	var code string
	*counter = nowSec / *step
	code, err = genHOTP()
	return code, err
}

func validateHOTP() bool {
	if len(otp) != *digits {
		return false
	}
	key, err = getKey()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error decoding secret: %s", err)
		os.Exit(1)
	}
	for i := 0; i < *window; i++ {
		code, _ := genHOTP()
		if code == otp {
			return true
		}
		*counter++
	}
	return false
}

func validateTOTP() bool {
	if len(otp) != *digits {
		return false
	}
	key, err = getKey()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error decoding secret: %s", err)
		os.Exit(1)
	}
	min := (nowSec / *step) - int64((*window / 2))
	max := (nowSec / *step) + int64((*window / 2))
	for t := min; t <= max; t++ {
		code, _ := genTOTP()
		if code == otp {
			return true
		}
		nowSec += *step
	}
	return false
}

//// Helpers

func getKey() ([]byte, error) {
	var (
		key []byte
	)

	if *b32 {
		key, err = base32.StdEncoding.DecodeString(secret)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Err decoding secret: %s\n", err)
			return key, err
		}
	}
	if !*b32 {
		key, err = hex.DecodeString(secret)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Err decoding secret: %s\n", err)
			return key, err
		}
	}
	return key, nil
}

//// arg parsing functions

func addFlags(f *flag.FlagSet) {
	// common flags add in flagParse method
	b32 = f.Bool("b", false, "Use b32 encoding instead of hex")
	digits = f.Int("d", 6, "The number of digits in the OTP")
	window = f.Int("w", 0, "Window of counter values to test when validating OTPs")
	verbose = f.Bool("v", false, "Explain what is being done.")
}

func getPositionalArgs(f *flag.FlagSet) {
	secret = strings.ToUpper(f.Arg(0))
	secret = strings.Replace(secret, " ", "", -1)
	if secret == "" {
		usage()
	}

	if *b32 {
		// repad base 32 strings if they are short.
		for len(secret) < 32 {
			secret = secret + "="
		}
	}

	otp = f.Arg(1)
}

func parseFlags(f *flag.FlagSet) {
	addFlags(f)
	err = f.Parse(os.Args[2:])
	if err != nil {
		os.Exit(1)
	}
	getPositionalArgs(f)
}
<|endoftext|>


<commit_before>package beanstalk

import (
	"context"
	"fmt"
	"sync"
	"time"
)

// Consumer maintains a pool of connections and allows workers to reserve jobs
// on those connections.
type Consumer struct {
	uris     []string
	tubes    []string
	config   Config
	reserveC chan chan *Job
	wg       sync.WaitGroup
}

// NewConsumer returns a new Consumer.
func NewConsumer(uris []string, tubes []string, config Config) (*Consumer, error) {
	if err := validURIs(uris); err != nil {
		return nil, err
	}

	return &Consumer{
		uris:     uris,
		tubes:    tubes,
		config:   config.normalize(),
		reserveC: make(chan chan *Job, config.NumGoroutines),
	}, nil
}

// Receive calls fn for each job it can reserve.
func (consumer *Consumer) Receive(ctx context.Context, fn func(ctx context.Context, job *Job)) {
	// Spin up the connections to the beanstalk servers.
	for _, uri := range multiply(consumer.uris, consumer.config.Multiply) {
		go maintainConn(ctx, uri, consumer.config, connHandler{
			setup:  consumer.watchTubes,
			handle: consumer.reserveJobs,
		})
	}

	// Spin up the workers in their own goroutine.
	for i := 0; i < consumer.config.NumGoroutines; i++ {
		consumer.wg.Add(1)

		go func() {
			defer consumer.wg.Done()
			consumer.worker(ctx, fn)
		}()
	}

	// Wait for all the workers to finish before returning.
	consumer.wg.Wait()
}

// worker calls fn for every job it can reserve.
func (consumer *Consumer) worker(ctx context.Context, fn func(ctx context.Context, job *Job)) {
	jobC := make(chan *Job)

	for {
		// Add a reserve request to the reserve channel.
		consumer.reserveC <- jobC

		select {
		// If a reserved job comes in, pass it to fn.
		case job := <-jobC:
			func() {
				defer job.Release(context.Background())
				fn(context.Background(), job)
			}()

		// Stop if the context was cancelled.
		case <-ctx.Done():
			return
		}
	}
}

// watchTubes watches the requested tubes.
func (consumer *Consumer) watchTubes(ctx context.Context, conn *Conn) error {
	if len(consumer.tubes) == 0 {
		return nil
	}

	// Watch all the requested tubes.
	for _, tube := range consumer.tubes {
		if err := conn.Watch(ctx, tube); err != nil {
			return fmt.Errorf("error watching tube: %s: %s", tube, err)
		}
	}

	// Ignore the default tube, unless it was explicitly requested.
	if !includes(consumer.tubes, "default") {
		if err := conn.Ignore(ctx, "default"); err != nil {
			return fmt.Errorf("error ignoring default tube: %s", err)
		}
	}

	return nil
}

// reserveJobs is responsible for reserving jobs on demand and pass them back
// to the worker method that will call its worker function with it.
func (consumer *Consumer) reserveJobs(ctx context.Context, conn *Conn) error {
	var err error

	// If the return error is nil, then the context was cancelled. However,
	// reserved jobs on this connection might still need to finish up so wait for
	// the workers to finish before returning (and thus closing) the connection.
	defer func() {
		if err == nil {
			consumer.wg.Wait()
		}
	}()

	var job *Job
	var jobC chan *Job
	for {
		// Wait for a reserve request from a worker to come in.
		select {
		case jobC = <-consumer.reserveC:
		case <-ctx.Done():
			return nil
		}

		// Attempt to reserve a job.
		if job, err = conn.ReserveWithTimeout(ctx, 0); job != nil {
			select {
			// Return the job to the worker and wait for another reserve request.
			case jobC <- job:
				continue

			// Release the job and stop if the context was cancelled.
			case <-ctx.Done():
				if err = job.Release(ctx); err != nil {
					consumer.config.ErrorFunc(err, "Unable to release job after context was cancelled")
				}

				return nil
			}
		}

		// Put the reserve request back and return if an error occurred.
		consumer.reserveC <- jobC
		if err != nil {
			return err
		}

		// The watched tubes are empty, so wait a bit before reserving again.
		select {
		case <-time.After(consumer.config.ReserveTimeout):
		case <-ctx.Done():
			return nil
		}
	}
}
<commit_msg>Don't call job.Release() with a cancelled context<commit_after>package beanstalk

import (
	"context"
	"fmt"
	"sync"
	"time"
)

// Consumer maintains a pool of connections and allows workers to reserve jobs
// on those connections.
type Consumer struct {
	uris     []string
	tubes    []string
	config   Config
	reserveC chan chan *Job
	wg       sync.WaitGroup
}

// NewConsumer returns a new Consumer.
func NewConsumer(uris []string, tubes []string, config Config) (*Consumer, error) {
	if err := validURIs(uris); err != nil {
		return nil, err
	}

	return &Consumer{
		uris:     uris,
		tubes:    tubes,
		config:   config.normalize(),
		reserveC: make(chan chan *Job, config.NumGoroutines),
	}, nil
}

// Receive calls fn for each job it can reserve.
func (consumer *Consumer) Receive(ctx context.Context, fn func(ctx context.Context, job *Job)) {
	// Spin up the connections to the beanstalk servers.
	for _, uri := range multiply(consumer.uris, consumer.config.Multiply) {
		go maintainConn(ctx, uri, consumer.config, connHandler{
			setup:  consumer.watchTubes,
			handle: consumer.reserveJobs,
		})
	}

	// Spin up the workers in their own goroutine.
	for i := 0; i < consumer.config.NumGoroutines; i++ {
		consumer.wg.Add(1)

		go func() {
			defer consumer.wg.Done()
			consumer.worker(ctx, fn)
		}()
	}

	// Wait for all the workers to finish before returning.
	consumer.wg.Wait()
}

// worker calls fn for every job it can reserve.
func (consumer *Consumer) worker(ctx context.Context, fn func(ctx context.Context, job *Job)) {
	jobC := make(chan *Job)

	for {
		// Add a reserve request to the reserve channel.
		consumer.reserveC <- jobC

		select {
		// If a reserved job comes in, pass it to fn.
		case job := <-jobC:
			func() {
				defer job.Release(context.Background())
				fn(context.Background(), job)
			}()

		// Stop if the context was cancelled.
		case <-ctx.Done():
			return
		}
	}
}

// watchTubes watches the requested tubes.
func (consumer *Consumer) watchTubes(ctx context.Context, conn *Conn) error {
	if len(consumer.tubes) == 0 {
		return nil
	}

	// Watch all the requested tubes.
	for _, tube := range consumer.tubes {
		if err := conn.Watch(ctx, tube); err != nil {
			return fmt.Errorf("error watching tube: %s: %s", tube, err)
		}
	}

	// Ignore the default tube, unless it was explicitly requested.
	if !includes(consumer.tubes, "default") {
		if err := conn.Ignore(ctx, "default"); err != nil {
			return fmt.Errorf("error ignoring default tube: %s", err)
		}
	}

	return nil
}

// reserveJobs is responsible for reserving jobs on demand and pass them back
// to the worker method that will call its worker function with it.
func (consumer *Consumer) reserveJobs(ctx context.Context, conn *Conn) error {
	var err error

	// If the return error is nil, then the context was cancelled. However,
	// reserved jobs on this connection might still need to finish up so wait for
	// the workers to finish before returning (and thus closing) the connection.
	defer func() {
		if err == nil {
			consumer.wg.Wait()
		}
	}()

	var job *Job
	var jobC chan *Job
	for {
		// Wait for a reserve request from a worker to come in.
		select {
		case jobC = <-consumer.reserveC:
		case <-ctx.Done():
			return nil
		}

		// Attempt to reserve a job.
		if job, err = conn.ReserveWithTimeout(ctx, 0); job != nil {
			select {
			// Return the job to the worker and wait for another reserve request.
			case jobC <- job:
				continue

			// Release the job and stop if the context was cancelled.
			case <-ctx.Done():
				if err = job.Release(context.Background()); err != nil {
					consumer.config.ErrorFunc(err, "Unable to release job after context was cancelled")
				}

				return nil
			}
		}

		// Put the reserve request back and return if an error occurred.
		consumer.reserveC <- jobC
		if err != nil {
			return err
		}

		// The watched tubes are empty, so wait a bit before reserving again.
		select {
		case <-time.After(consumer.config.ReserveTimeout):
		case <-ctx.Done():
			return nil
		}
	}
}
<|endoftext|>


<commit_before>package comm

import (
	"net/rpc"
	"sync"

	"github.com/mateusbraga/freestore/pkg/view"
)

var (
	rpcClientWorkers   = make(map[view.Process]rpcClientInstance)
	rpcClientWorkersMu sync.Mutex
)

type rpcClientJob struct {
	serviceMethod string
	args          interface{}
	reply         interface{}
	errorCh       chan error
}

type rpcClientInstance struct {
	process view.Process
	jobChan chan rpcClientJob
}

// TODO code when we terminate the workers
func rpcClientWorker(rci rpcClientInstance) {
	process := rci.process
	jobChan := rci.jobChan

	var client *rpc.Client
	var err error

	for {
		job, ok := <-jobChan
		if !ok {
			// terminate goroutine if jobChan is closed
			return
		}

		// make sure client is not null
		if client == nil {
			client, err = rpc.Dial("tcp", process.Addr)
			if err != nil {
				job.errorCh <- err
				continue
			}
		}

		err = client.Call(job.serviceMethod, job.args, job.reply)
		if err != nil {
			if err == rpc.ErrShutdown {
				client, err = rpc.Dial("tcp", process.Addr)
				if err != nil {
					job.errorCh <- err
					continue
				}

				// retry this job
				go func() { jobChan <- job }()

			} else {
				job.errorCh <- err
			}
		}
		job.errorCh <- nil
	}
}

func getRpcClientInstance(process view.Process) rpcClientInstance {
	rpcClientWorkersMu.Lock()
	defer rpcClientWorkersMu.Unlock()

	rci, ok := rpcClientWorkers[process]
	if !ok {
		// worker does not exist, create one
		rci = rpcClientInstance{process: process, jobChan: make(chan rpcClientJob, 20)}
		rpcClientWorkers[process] = rci
		go rpcClientWorker(rci)
	}
	return rci
}

func SendRPCRequest(process view.Process, serviceMethod string, args interface{}, reply interface{}) error {
	rci := getRpcClientInstance(process)

	errorCh := make(chan error)
	rcj := rpcClientJob{serviceMethod: serviceMethod, args: args, reply: reply, errorCh: errorCh}
	rci.jobChan <- rcj

	err := <-errorCh
	return err
}
<commit_msg>Remove TODO on ending workers<commit_after>package comm

import (
	"net/rpc"
	"sync"

	"github.com/mateusbraga/freestore/pkg/view"
)

var (
	rpcClientWorkers   = make(map[view.Process]rpcClientInstance)
	rpcClientWorkersMu sync.Mutex
)

type rpcClientJob struct {
	serviceMethod string
	args          interface{}
	reply         interface{}
	errorCh       chan error
}

type rpcClientInstance struct {
	process view.Process
	jobChan chan rpcClientJob
}

// rpcClientWorker is responsable for maintaining a rpc.Client.
// Currently, rpcClientWorker never ends so we can keep it simpler for now.
func rpcClientWorker(rci rpcClientInstance) {
	process := rci.process
	jobChan := rci.jobChan

	var client *rpc.Client
	var err error

	for {
		job := <-jobChan

		// make sure client is not null
		if client == nil {
			client, err = rpc.Dial("tcp", process.Addr)
			if err != nil {
				job.errorCh <- err
				continue
			}
		}

		err = client.Call(job.serviceMethod, job.args, job.reply)
		if err != nil {
			if err == rpc.ErrShutdown {
				client, err = rpc.Dial("tcp", process.Addr)
				if err != nil {
					job.errorCh <- err
					continue
				}

				// retry this job
				go func() { jobChan <- job }()

			} else {
				job.errorCh <- err
			}
		}
		job.errorCh <- nil
	}
}

func getRpcClientInstance(process view.Process) rpcClientInstance {
	rpcClientWorkersMu.Lock()
	defer rpcClientWorkersMu.Unlock()

	rci, ok := rpcClientWorkers[process]
	if !ok {
		// worker does not exist, create one
		rci = rpcClientInstance{process: process, jobChan: make(chan rpcClientJob, 20)}
		rpcClientWorkers[process] = rci
		go rpcClientWorker(rci)
	}
	return rci
}

func SendRPCRequest(process view.Process, serviceMethod string, args interface{}, reply interface{}) error {
	rci := getRpcClientInstance(process)

	errorCh := make(chan error)
	rcj := rpcClientJob{serviceMethod: serviceMethod, args: args, reply: reply, errorCh: errorCh}
	rci.jobChan <- rcj

	err := <-errorCh
	return err
}
<|endoftext|>


<commit_before>package io

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"strings"
	"unicode"

	"github.com/gonum/plot/plotter"
)

type Output struct {
	w io.Writer

	caseN int
	input *Input

	output *bytes.Buffer

	periodicPrint       chan struct{}
	previousPeriodicInt int
	periodicCount       int
	prevPeriodicCount   int

	points plotter.XYs
}

func newOutput(w io.Writer) *Output {
	return &Output{
		w:             w,
		output:        &bytes.Buffer{},
		periodicPrint: make(chan struct{}, 10),
	}
}

func (o *Output) resetPeriodic() {
	for {
		select {
		case <-o.periodicPrint:
		default:
			return
		}
	}
}

func (o *Output) triggerPeriodic() {
	o.resetPeriodic()
	o.periodicPrint <- struct{}{}
}

func (o *Output) Print(a ...interface{}) {
	fmt.Fprint(o.output, a...)
}

func (o *Output) Printf(format string, a ...interface{}) {
	fmt.Fprintf(o.output, format, a...)
}

func (o *Output) Fatal(a ...interface{}) {
	o.DebugCase()
	log.Fatalln(a...)
}

func (o *Output) Fatalf(format string, a ...interface{}) {
	o.DebugCase()
	log.Fatalf(format, a...)
}

func (o *Output) Debug(a ...interface{}) {
	o.DebugCase()
	log.Println(a...)
}

func (o *Output) Debugf(format string, a ...interface{}) {
	o.DebugCase()
	log.Printf(format, a...)
}

func (o *Output) DebugCase() {
	log.Printf("Case #%d, input: %v, output: %q\n", o.caseN, o.input.currentCase(), string(o.output.Bytes()))
}

func (o *Output) Periodic(a ...interface{}) {
	select {
	case <-o.periodicPrint:
		log.Println(a...)
	default:
	}
}

func (o *Output) PeriodicInt(a int) {
	select {
	case <-o.periodicPrint:
		log.Println(a, "Rate =", a-o.previousPeriodicInt)
		o.previousPeriodicInt = a
	default:
	}
}

func (o *Output) PeriodicCount() {
	o.periodicCount++
	select {
	case <-o.periodicPrint:
		c := o.periodicCount
		log.Println(c, "Rate =", c-o.prevPeriodicCount)
		o.prevPeriodicCount = c
	default:
	}
}

func (o *Output) Periodicf(format string, a ...interface{}) {
	select {
	case <-o.periodicPrint:
		log.Printf(format, a...)
	default:
	}
}

func (o *Output) Point(x, y float64) {
	o.points = append(o.points, struct{ X, Y float64 }{
		X: x,
		Y: y,
	})
}

func (o *Output) PointInt(x, y int) {
	o.Point(float64(x), float64(y))
}

func (o *Output) init(input *Input, caseN int) {
	o.input = input
	o.caseN = caseN
}

func (o *Output) flush() {
	if o.output.Len() <= 0 {
		o.Fatal("No output")
	}
	fmt.Fprintf(o.w, "Case #%d:", o.caseN)
	if !unicode.In(rune(o.output.Bytes()[0]), unicode.White_Space) {
		o.w.Write([]byte{' '})
	}
	o.w.Write(o.output.Bytes())
	if o.output.Bytes()[o.output.Len()-1] != '\n' {
		o.w.Write([]byte{'\n'})
	}
	o.output.Reset()
}

func (o *Output) assertOutput(fatal []bool, a ...interface{}) {
	output := o.Debug
	if len(fatal) > 0 && fatal[0] {
		output = o.Fatal
	}
	output(a...)
}

func (o *Output) assertOutputf(fatal []bool, format string, a ...interface{}) {
	outputf := o.Debugf
	if len(fatal) > 0 && fatal[0] {
		outputf = o.Fatalf
	}
	outputf(format, a...)
}

func (o *Output) AssertByteCount(a byte, count int, fatal ...bool) {
	for _, b := range o.output.Bytes() {
		if a == b {
			count--
		}
	}
	if count != 0 {
		o.assertOutputf(fatal, "AssertByteCount: byte: %q difference: %d", string(a), count*-1)
	}
}

func (o *Output) AssertCount(count int, fatal ...bool) {
	if o.output.Len() != count {
		o.assertOutput(fatal, "AssertCount: ", count)
	}
}

func (o *Output) AssertEqual(data string, fatal ...bool) {
	if strings.TrimSpace(string(o.output.Bytes())) != strings.TrimSpace(data) {
		o.assertOutputf(fatal, "Output should be: %q", data)
	}
}

func (o *Output) AssertIntEqual(a, b int, fatal ...bool) {
	if a != b {
		o.assertOutputf(fatal, "Ints %d and %b not equal", a, b)
	}
}

func (o *Output) AssertTrue(a bool, fatal ...bool) {
	if !a {
		o.assertOutput(fatal, "Not true")
	}
}
<commit_msg>Fix assertIntEqual format<commit_after>package io

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"strings"
	"unicode"

	"github.com/gonum/plot/plotter"
)

type Output struct {
	w io.Writer

	caseN int
	input *Input

	output *bytes.Buffer

	periodicPrint       chan struct{}
	previousPeriodicInt int
	periodicCount       int
	prevPeriodicCount   int

	points plotter.XYs
}

func newOutput(w io.Writer) *Output {
	return &Output{
		w:             w,
		output:        &bytes.Buffer{},
		periodicPrint: make(chan struct{}, 10),
	}
}

func (o *Output) resetPeriodic() {
	for {
		select {
		case <-o.periodicPrint:
		default:
			return
		}
	}
}

func (o *Output) triggerPeriodic() {
	o.resetPeriodic()
	o.periodicPrint <- struct{}{}
}

func (o *Output) Print(a ...interface{}) {
	fmt.Fprint(o.output, a...)
}

func (o *Output) Printf(format string, a ...interface{}) {
	fmt.Fprintf(o.output, format, a...)
}

func (o *Output) Fatal(a ...interface{}) {
	o.DebugCase()
	log.Fatalln(a...)
}

func (o *Output) Fatalf(format string, a ...interface{}) {
	o.DebugCase()
	log.Fatalf(format, a...)
}

func (o *Output) Debug(a ...interface{}) {
	o.DebugCase()
	log.Println(a...)
}

func (o *Output) Debugf(format string, a ...interface{}) {
	o.DebugCase()
	log.Printf(format, a...)
}

func (o *Output) DebugCase() {
	log.Printf("Case #%d, input: %v, output: %q\n", o.caseN, o.input.currentCase(), string(o.output.Bytes()))
}

func (o *Output) Periodic(a ...interface{}) {
	select {
	case <-o.periodicPrint:
		log.Println(a...)
	default:
	}
}

func (o *Output) PeriodicInt(a int) {
	select {
	case <-o.periodicPrint:
		log.Println(a, "Rate =", a-o.previousPeriodicInt)
		o.previousPeriodicInt = a
	default:
	}
}

func (o *Output) PeriodicCount() {
	o.periodicCount++
	select {
	case <-o.periodicPrint:
		c := o.periodicCount
		log.Println(c, "Rate =", c-o.prevPeriodicCount)
		o.prevPeriodicCount = c
	default:
	}
}

func (o *Output) Periodicf(format string, a ...interface{}) {
	select {
	case <-o.periodicPrint:
		log.Printf(format, a...)
	default:
	}
}

func (o *Output) Point(x, y float64) {
	o.points = append(o.points, struct{ X, Y float64 }{
		X: x,
		Y: y,
	})
}

func (o *Output) PointInt(x, y int) {
	o.Point(float64(x), float64(y))
}

func (o *Output) init(input *Input, caseN int) {
	o.input = input
	o.caseN = caseN
}

func (o *Output) flush() {
	if o.output.Len() <= 0 {
		o.Fatal("No output")
	}
	fmt.Fprintf(o.w, "Case #%d:", o.caseN)
	if !unicode.In(rune(o.output.Bytes()[0]), unicode.White_Space) {
		o.w.Write([]byte{' '})
	}
	o.w.Write(o.output.Bytes())
	if o.output.Bytes()[o.output.Len()-1] != '\n' {
		o.w.Write([]byte{'\n'})
	}
	o.output.Reset()
}

func (o *Output) assertOutput(fatal []bool, a ...interface{}) {
	output := o.Debug
	if len(fatal) > 0 && fatal[0] {
		output = o.Fatal
	}
	output(a...)
}

func (o *Output) assertOutputf(fatal []bool, format string, a ...interface{}) {
	outputf := o.Debugf
	if len(fatal) > 0 && fatal[0] {
		outputf = o.Fatalf
	}
	outputf(format, a...)
}

func (o *Output) AssertByteCount(a byte, count int, fatal ...bool) {
	for _, b := range o.output.Bytes() {
		if a == b {
			count--
		}
	}
	if count != 0 {
		o.assertOutputf(fatal, "AssertByteCount: byte: %q difference: %d", string(a), count*-1)
	}
}

func (o *Output) AssertCount(count int, fatal ...bool) {
	if o.output.Len() != count {
		o.assertOutput(fatal, "AssertCount: ", count)
	}
}

func (o *Output) AssertEqual(data string, fatal ...bool) {
	if strings.TrimSpace(string(o.output.Bytes())) != strings.TrimSpace(data) {
		o.assertOutputf(fatal, "Output should be: %q", data)
	}
}

func (o *Output) AssertIntEqual(a, b int, fatal ...bool) {
	if a != b {
		o.assertOutputf(fatal, "Ints %d and %d not equal", a, b)
	}
}

func (o *Output) AssertTrue(a bool, fatal ...bool) {
	if !a {
		o.assertOutput(fatal, "Not true")
	}
}
<|endoftext|>


<commit_before>package config

import (
	"github.com/skygeario/skygear-server/pkg/core/validation"
)

const (
	// Schemas is public so that the schemas can be composed in other contexts.
	Schemas = `
{
	"NonEmptyString": {
		"$id": "#NonEmptyString",
		"type": "string",
		"minLength": 1
	},
	"NonNegativeInteger": {
		"$id": "#NonNegativeInteger",
		"type": "integer",
		"minimum": 0
	},
	"UserConfiguration": {
		"$id": "#UserConfiguration",
		"type": "object",
		"properties": {
			"clients": {
				"type": "array",
				"items": { "$ref": "#APIClientConfiguration" }
			},
			"master_key": { "$ref": "#NonEmptyString" },
			"url_prefix": {
				"type": "string",
				"format": "URLFullOnly"
			},
			"cors": { "$ref": "#CORSConfiguration" },
			"auth": { "$ref": "#AuthConfiguration" },
			"mfa": { "$ref": "#MFAConfiguration" },
			"user_audit": { "$ref": "#UserAuditConfiguration" },
			"password_policy": { "$ref": "#PasswordPolicyConfiguration" },
			"forgot_password": { "$ref": "#ForgotPasswordConfiguration" },
			"welcome_email": { "$ref": "#WelcomeEmailConfiguration" },
			"sso": { "$ref": "#SSOConfiguration" },
			"user_verification": { "$ref": "#UserVerificationConfiguration" },
			"hook": { "$ref": "#HookUserConfiguration" },
			"smtp" : { "$ref": "#SMTPConfiguration" },
			"twilio" : { "$ref": "#TwilioConfiguration" },
			"nexmo" : { "$ref": "#NexmoConfiguration" },
			"asset": { "$ref": "#AssetConfiguration" }
		},
		"required": ["master_key", "auth", "hook", "asset"]
	},
	"AssetConfiguration": {
		"$id": "#AssetConfiguration",
		"type": "object",
		"properties": {
			"secret": { "$ref": "#NonEmptyString" }
		},
		"required": ["secret"]
	},
	"APIClientConfiguration": {
		"$id": "#APIClientConfiguration",
		"type": "object",
		"properties": {
			"id": { "$ref": "#NonEmptyString" },
			"name": { "$ref": "#NonEmptyString" },
			"api_key": { "$ref": "#NonEmptyString" },
			"session_transport": {
				"type": "string",
				"enum": ["header", "cookie"]
			},
			"access_token_lifetime": { "type": "integer", "minimum": 0 },
			"session_idle_timeout_enabled": { "type": "boolean" },
			"session_idle_timeout": { "type": "integer", "minimum": 0 },
			"refresh_token_disabled": { "type": "boolean" },
			"refresh_token_lifetime": { "type": "integer", "minimum": 0 }
		},
		"required": ["id", "name", "api_key", "session_transport"]
	},
	"CORSConfiguration": {
		"$id": "#CORSConfiguration",
		"type": "object",
		"properties": {
			"origin": { "$ref": "#NonEmptyString" }
		},
		"required": ["origin"]
	},
	"AuthConfiguration": {
		"$id": "#AuthConfiguration",
		"type": "object",
		"properties": {
			"authentication_session": { "$ref": "#AuthenticationSessionConfiguration" },
			"login_id_keys": {
				"type": "array",
				"minItems": 1,
				"items": { "$ref": "#LoginIDKeyConfiguration" }
			}
		},
		"required": ["authentication_session"]
	},
	"AuthenticationSessionConfiguration": {
		"$id": "#AuthenticationSessionConfiguration",
		"type": "object",
		"properties": {
			"secret": { "$ref": "#NonEmptyString" }
		},
		"required": ["secret"]
	},
	"MFAConfiguration": {
		"$id": "#MFAConfiguration",
		"type": "object",
		"properties": {
			"enforcement": {
				"type": "string",
				"enum": ["off", "optional", "required"]
			},
			"maximum": {
				"type": "integer",
				"minimum": 0,
				"maximum": 15
			},
			"totp": {
				"type": "object",
				"properties": {
					"maximum": {
						"type": "integer",
						"minimum": 0,
						"maximum": 5
					}
				}
			},
			"oob": {
				"type": "object",
				"properties": {
					"sms": {
						"type": "object",
						"properties": {
							"maximum": {
								"type": "integer",
								"minimum": 0,
								"maximum": 5
							}
						}
					},
					"email": {
						"type": "object",
						"properties": {
							"maximum": {
								"type": "integer",
								"minimum": 0,
								"maximum": 5
							}
						}
					}
				}
			},
			"bearer_token": {
				"type": "object",
				"properties": {
					"expire_in_days": {
						"type": "integer",
						"minimum": 1,
						"maximum": 3650
					}
				}
			},
			"recovery_code": {
				"type": "object",
				"properties": {
					"count": {
						"type": "integer",
						"minimum": 8,
						"maximum": 24
					},
					"list_enabled": {
						"type": "boolean"
					}
				}
			}
		}
	},
	"LoginIDKeyConfiguration": {
		"$id": "#LoginIDKeyConfiguration",
		"type": "object",
		"properties": {
			"key": { "$ref": "#NonEmptyString" },
			"type": {
				"type": "string",
				"enum": ["raw", "email", "phone"]
			},
			"minimum": { "$ref": "#NonNegativeInteger" },
			"maximum": { "$ref": "#NonNegativeInteger" }
		},
		"required": ["type"]
	},
	"UserAuditConfiguration": {
		"$id": "#UserAuditConfiguration",
		"type": "object",
		"properties": {
			"enabled": { "type": "boolean" },
			"trail_handler_url": { "type": "string" }
		}
	},
	"PasswordPolicyConfiguration": {
		"$id": "#PasswordPolicyConfiguration",
		"type": "object",
		"properties": {
			"min_length": { "$ref": "#NonNegativeInteger" },
			"uppercase_required": { "type": "boolean" },
			"lowercase_required": { "type": "boolean" },
			"digit_required": { "type": "boolean" },
			"symbol_required": { "type": "boolean" },
			"minimum_guessable_level": {
				"type": "integer",
				"minimum": 0,
				"maximum": 4
			},
			"excluded_keywords": {
				"type": "array",
				"items": { "type": "string" }
			},
			"history_size": { "$ref": "#NonNegativeInteger" },
			"history_days": { "$ref": "#NonNegativeInteger" },
			"expiry_days": { "$ref": "#NonNegativeInteger" }
		}
	},
	"ForgotPasswordConfiguration": {
		"$id": "#ForgotPasswordConfiguration",
		"type": "object",
		"properties": {
			"app_name": { "type": "string" },
			"url_prefix": {
				"type": "string",
				"format": "URLFullOnly"
			},
			"secure_match": { "type": "boolean" },
			"sender": { "type": "string" },
			"reply_to": { "type": "string" },
			"subject": { "type": "string" },
			"reset_url_lifetime": { "$ref": "#NonNegativeInteger" },
			"success_redirect": { "type": "string" },
			"error_redirect": { "type": "string" },
			"email_text_url": { "type": "string" },
			"email_html_url": { "type": "string" },
			"reset_html_url": { "type": "string" },
			"reset_success_html_url": { "type": "string" },
			"reset_error_html_url": { "type": "string" }
		}
	},
	"WelcomeEmailConfiguration": {
		"$id": "#WelcomeEmailConfiguration",
		"type": "object",
		"properties": {
			"enabled": { "type": "boolean" },
			"url_prefix": { "type": "string" },
			"sender": { "type": "string" },
			"reply_to": { "type": "string" },
			"subject": { "type": "string" },
			"text_url": { "type": "string" },
			"html_url": { "type": "string" },
			"destination": {
				"type": "string",
				"enum": ["first", "all"]
			}
		}
	},
	"SSOConfiguration": {
		"$id": "#SSOConfiguration",
		"type": "object",
		"properties": {
			"custom_token": { "$ref": "#CustomTokenConfiguration" },
			"oauth": { "$ref": "#OAuthConfiguration" }
		},
		"required": ["custom_token"]
	},
	"CustomTokenConfiguration": {
		"$id": "#CustomTokenConfiguration",
		"type": "object",
		"properties": {
			"enabled": { "type": "boolean" },
			"issuer": { "$ref": "#NonEmptyString" },
			"audience": { "$ref": "#NonEmptyString" },
			"secret": { "$ref": "#NonEmptyString" },
			"on_user_duplicate_allow_merge": { "type": "boolean" },
			"on_user_duplicate_allow_create": { "type": "boolean" }
		},
		"if": {
			"properties": {
				"enabled": {
					"const": true
				}
			},
			"required": ["enabled"]
		},
		"then": {
			"required": ["issuer", "audience", "secret"]
		},
		"else": {
			"required": ["secret"]
		}
	},
	"OAuthConfiguration": {
		"$id": "#OAuthConfiguration",
		"type": "object",
		"properties": {
			"url_prefix": { "type": "string" },
			"state_jwt_secret": { "type": "string" },
			"allowed_callback_urls": {
				"type": "array",
				"items": { "type": "string" }
			},
			"external_access_token_flow_enabled": { "type": "boolean" },
			"on_user_duplicate_allow_merge": { "type": "boolean" },
			"on_user_duplicate_allow_create": { "type": "boolean" },
			"providers": {
				"type": "array",
				"items": { "$ref": "#OAuthProviderConfiguration" }
			}
		},
		"if": {
			"properties": {
				"providers": {
					"type": "array",
					"minItems": 1
				}
			},
			"required": ["providers"]
		},
		"then": {
			"properties": {
				"allowed_callback_urls": {
					"minItems": 1
				}
			},
			"required": ["state_jwt_secret", "allowed_callback_urls"]
		},
		"else": {
			"required": ["state_jwt_secret"]
		}
	},
	"OAuthProviderConfiguration": {
		"$id": "#OAuthProviderConfiguration",
		"type": "object",
		"properties": {
			"id": { "type": "string" },
			"type": {
				"type": "string",
				"enum": ["google", "facebook", "instagram", "linkedin", "azureadv2"]
			},
			"client_id": { "type": "string" },
			"client_secret": { "type": "string" },
			"scope": { "type": "string" },
			"tenant": { "type": "string" }
		},
		"if": {
			"properties": {
				"type": { "const": "azureadv2" }
			},
			"required": ["type"]
		},
		"then": {
			"required": ["type", "client_id", "client_secret", "tenant"]
		},
		"else": {
			"required": ["type", "client_id", "client_secret"]
		}
	},
	"UserVerificationConfiguration": {
		"$id": "#UserVerificationConfiguration",
		"type": "object",
		"properties": {
			"url_prefix": {
				"type": "string",
				"format": "URLFullOnly"
			},
			"auto_send_on_signup": { "type": "boolean" },
			"criteria": {
				"type": "string",
				"enum": ["any", "all"]
			},
			"error_redirect": { "type": "string" },
			"error_html_url": { "type": "string" },
			"login_id_keys": {
				"type": "array",
				"items": { "$ref": "#UserVerificationKeyConfiguration" }
			}
		}
	},
	"UserVerificationKeyConfiguration": {
		"$id": "#UserVerificationKeyConfiguration",
		"type": "object",
		"properties": {
			"key": { "$ref": "#NonEmptyString" },
			"code_format": {
				"type": "string",
				"enum": ["numeric", "complex"]
			},
			"expiry": { "$ref": "#NonNegativeInteger" },
			"success_redirect": { "type": "string" },
			"success_html_url": { "type": "string" },
			"error_redirect": { "type": "string" },
			"error_html_url": { "type": "string" },
			"provider_config": { "$ref": "#UserVerificationProviderConfiguration" }
		}
	},
	"UserVerificationProviderConfiguration": {
		"$id": "#UserVerificationProviderConfiguration",
		"type": "object",
		"properties": {
			"subject": { "type": "string" },
			"sender": { "type": "string" },
			"reply_to": { "type": "string" },
			"text_url": { "type": "string" },
			"html_url": { "type": "string" }
		}
	},
	"HookUserConfiguration": {
		"$id": "#HookUserConfiguration",
		"type": "object",
		"properties": {
			"secret": { "$ref": "#NonEmptyString" }
		},
		"required": ["secret"]
	},
	"SMTPConfiguration": {
		"$id": "#SMTPConfiguration",
		"type": "object",
		"properties": {
			"host": { "type": "string" },
			"port": { "$ref": "#NonNegativeInteger" },
			"mode": {
				"type": "string",
				"enum": ["normal", "ssl"]
			},
			"login": { "type": "string" },
			"password": { "type": "string" }
		}
	},
	"TwilioConfiguration": {
		"$id": "#TwilioConfiguration",
		"type": "object",
		"properties": {
			"account_sid": { "type": "string" },
			"auth_token": { "type": "string" },
			"from": { "type": "string" }
		}
	},
	"NexmoConfiguration": {
		"$id": "#NexmoConfiguration",
		"type": "object",
		"properties": {
			"api_key": { "type": "string" },
			"api_secret": { "type": "string" },
			"from": { "type": "string" }
		}
	}
}
`
)

var (
	userConfigurationValidator *validation.Validator
)

func init() {
	userConfigurationValidator = validation.NewValidator("http://v2.skygear.io")
	userConfigurationValidator.AddSchemaFragments(Schemas)
}

func ValidateUserConfiguration(value interface{}) error {
	return userConfigurationValidator.ValidateGoValue("#UserConfiguration", value)
}
<commit_msg>Update UserConfiguration schema to remove legacy fields<commit_after>package config

import (
	"github.com/skygeario/skygear-server/pkg/core/validation"
)

const (
	// Schemas is public so that the schemas can be composed in other contexts.
	Schemas = `
{
	"NonEmptyString": {
		"$id": "#NonEmptyString",
		"type": "string",
		"minLength": 1
	},
	"NonNegativeInteger": {
		"$id": "#NonNegativeInteger",
		"type": "integer",
		"minimum": 0
	},
	"UserConfiguration": {
		"$id": "#UserConfiguration",
		"type": "object",
		"properties": {
			"clients": {
				"type": "array",
				"items": { "$ref": "#APIClientConfiguration" }
			},
			"master_key": { "$ref": "#NonEmptyString" },
			"cors": { "$ref": "#CORSConfiguration" },
			"auth": { "$ref": "#AuthConfiguration" },
			"mfa": { "$ref": "#MFAConfiguration" },
			"user_audit": { "$ref": "#UserAuditConfiguration" },
			"password_policy": { "$ref": "#PasswordPolicyConfiguration" },
			"forgot_password": { "$ref": "#ForgotPasswordConfiguration" },
			"welcome_email": { "$ref": "#WelcomeEmailConfiguration" },
			"sso": { "$ref": "#SSOConfiguration" },
			"user_verification": { "$ref": "#UserVerificationConfiguration" },
			"hook": { "$ref": "#HookUserConfiguration" },
			"smtp" : { "$ref": "#SMTPConfiguration" },
			"twilio" : { "$ref": "#TwilioConfiguration" },
			"nexmo" : { "$ref": "#NexmoConfiguration" },
			"asset": { "$ref": "#AssetConfiguration" }
		},
		"required": ["master_key", "auth", "hook", "asset"]
	},
	"AssetConfiguration": {
		"$id": "#AssetConfiguration",
		"type": "object",
		"properties": {
			"secret": { "$ref": "#NonEmptyString" }
		},
		"required": ["secret"]
	},
	"APIClientConfiguration": {
		"$id": "#APIClientConfiguration",
		"type": "object",
		"properties": {
			"id": { "$ref": "#NonEmptyString" },
			"name": { "$ref": "#NonEmptyString" },
			"api_key": { "$ref": "#NonEmptyString" },
			"session_transport": {
				"type": "string",
				"enum": ["header", "cookie"]
			},
			"access_token_lifetime": { "type": "integer", "minimum": 0 },
			"session_idle_timeout_enabled": { "type": "boolean" },
			"session_idle_timeout": { "type": "integer", "minimum": 0 },
			"refresh_token_disabled": { "type": "boolean" },
			"refresh_token_lifetime": { "type": "integer", "minimum": 0 }
		},
		"required": ["id", "name", "api_key", "session_transport"]
	},
	"CORSConfiguration": {
		"$id": "#CORSConfiguration",
		"type": "object",
		"properties": {
			"origin": { "$ref": "#NonEmptyString" }
		},
		"required": ["origin"]
	},
	"AuthConfiguration": {
		"$id": "#AuthConfiguration",
		"type": "object",
		"properties": {
			"authentication_session": { "$ref": "#AuthenticationSessionConfiguration" },
			"login_id_keys": {
				"type": "array",
				"minItems": 1,
				"items": { "$ref": "#LoginIDKeyConfiguration" }
			}
		},
		"required": ["authentication_session"]
	},
	"AuthenticationSessionConfiguration": {
		"$id": "#AuthenticationSessionConfiguration",
		"type": "object",
		"properties": {
			"secret": { "$ref": "#NonEmptyString" }
		},
		"required": ["secret"]
	},
	"MFAConfiguration": {
		"$id": "#MFAConfiguration",
		"type": "object",
		"properties": {
			"enforcement": {
				"type": "string",
				"enum": ["off", "optional", "required"]
			},
			"maximum": {
				"type": "integer",
				"minimum": 0,
				"maximum": 15
			},
			"totp": {
				"type": "object",
				"properties": {
					"maximum": {
						"type": "integer",
						"minimum": 0,
						"maximum": 5
					}
				}
			},
			"oob": {
				"type": "object",
				"properties": {
					"sms": {
						"type": "object",
						"properties": {
							"maximum": {
								"type": "integer",
								"minimum": 0,
								"maximum": 5
							}
						}
					},
					"email": {
						"type": "object",
						"properties": {
							"maximum": {
								"type": "integer",
								"minimum": 0,
								"maximum": 5
							}
						}
					}
				}
			},
			"bearer_token": {
				"type": "object",
				"properties": {
					"expire_in_days": {
						"type": "integer",
						"minimum": 1,
						"maximum": 3650
					}
				}
			},
			"recovery_code": {
				"type": "object",
				"properties": {
					"count": {
						"type": "integer",
						"minimum": 8,
						"maximum": 24
					},
					"list_enabled": {
						"type": "boolean"
					}
				}
			}
		}
	},
	"LoginIDKeyConfiguration": {
		"$id": "#LoginIDKeyConfiguration",
		"type": "object",
		"properties": {
			"key": { "$ref": "#NonEmptyString" },
			"type": {
				"type": "string",
				"enum": ["raw", "email", "phone"]
			},
			"minimum": { "$ref": "#NonNegativeInteger" },
			"maximum": { "$ref": "#NonNegativeInteger" }
		},
		"required": ["type"]
	},
	"UserAuditConfiguration": {
		"$id": "#UserAuditConfiguration",
		"type": "object",
		"properties": {
			"enabled": { "type": "boolean" },
			"trail_handler_url": { "type": "string" }
		}
	},
	"PasswordPolicyConfiguration": {
		"$id": "#PasswordPolicyConfiguration",
		"type": "object",
		"properties": {
			"min_length": { "$ref": "#NonNegativeInteger" },
			"uppercase_required": { "type": "boolean" },
			"lowercase_required": { "type": "boolean" },
			"digit_required": { "type": "boolean" },
			"symbol_required": { "type": "boolean" },
			"minimum_guessable_level": {
				"type": "integer",
				"minimum": 0,
				"maximum": 4
			},
			"excluded_keywords": {
				"type": "array",
				"items": { "type": "string" }
			},
			"history_size": { "$ref": "#NonNegativeInteger" },
			"history_days": { "$ref": "#NonNegativeInteger" },
			"expiry_days": { "$ref": "#NonNegativeInteger" }
		}
	},
	"ForgotPasswordConfiguration": {
		"$id": "#ForgotPasswordConfiguration",
		"type": "object",
		"properties": {
			"app_name": { "type": "string" },
			"secure_match": { "type": "boolean" },
			"sender": { "type": "string" },
			"reply_to": { "type": "string" },
			"subject": { "type": "string" },
			"reset_url_lifetime": { "$ref": "#NonNegativeInteger" },
			"success_redirect": { "type": "string" },
			"error_redirect": { "type": "string" }
		}
	},
	"WelcomeEmailConfiguration": {
		"$id": "#WelcomeEmailConfiguration",
		"type": "object",
		"properties": {
			"enabled": { "type": "boolean" },
			"sender": { "type": "string" },
			"reply_to": { "type": "string" },
			"subject": { "type": "string" },
			"destination": {
				"type": "string",
				"enum": ["first", "all"]
			}
		}
	},
	"SSOConfiguration": {
		"$id": "#SSOConfiguration",
		"type": "object",
		"properties": {
			"custom_token": { "$ref": "#CustomTokenConfiguration" },
			"oauth": { "$ref": "#OAuthConfiguration" }
		},
		"required": ["custom_token"]
	},
	"CustomTokenConfiguration": {
		"$id": "#CustomTokenConfiguration",
		"type": "object",
		"properties": {
			"enabled": { "type": "boolean" },
			"issuer": { "$ref": "#NonEmptyString" },
			"audience": { "$ref": "#NonEmptyString" },
			"secret": { "$ref": "#NonEmptyString" },
			"on_user_duplicate_allow_merge": { "type": "boolean" },
			"on_user_duplicate_allow_create": { "type": "boolean" }
		},
		"if": {
			"properties": {
				"enabled": {
					"const": true
				}
			},
			"required": ["enabled"]
		},
		"then": {
			"required": ["issuer", "audience", "secret"]
		},
		"else": {
			"required": ["secret"]
		}
	},
	"OAuthConfiguration": {
		"$id": "#OAuthConfiguration",
		"type": "object",
		"properties": {
			"state_jwt_secret": { "type": "string" },
			"allowed_callback_urls": {
				"type": "array",
				"items": { "type": "string" }
			},
			"external_access_token_flow_enabled": { "type": "boolean" },
			"on_user_duplicate_allow_merge": { "type": "boolean" },
			"on_user_duplicate_allow_create": { "type": "boolean" },
			"providers": {
				"type": "array",
				"items": { "$ref": "#OAuthProviderConfiguration" }
			}
		},
		"if": {
			"properties": {
				"providers": {
					"type": "array",
					"minItems": 1
				}
			},
			"required": ["providers"]
		},
		"then": {
			"properties": {
				"allowed_callback_urls": {
					"minItems": 1
				}
			},
			"required": ["state_jwt_secret", "allowed_callback_urls"]
		},
		"else": {
			"required": ["state_jwt_secret"]
		}
	},
	"OAuthProviderConfiguration": {
		"$id": "#OAuthProviderConfiguration",
		"type": "object",
		"properties": {
			"id": { "type": "string" },
			"type": {
				"type": "string",
				"enum": ["google", "facebook", "instagram", "linkedin", "azureadv2"]
			},
			"client_id": { "type": "string" },
			"client_secret": { "type": "string" },
			"scope": { "type": "string" },
			"tenant": { "type": "string" }
		},
		"if": {
			"properties": {
				"type": { "const": "azureadv2" }
			},
			"required": ["type"]
		},
		"then": {
			"required": ["type", "client_id", "client_secret", "tenant"]
		},
		"else": {
			"required": ["type", "client_id", "client_secret"]
		}
	},
	"UserVerificationConfiguration": {
		"$id": "#UserVerificationConfiguration",
		"type": "object",
		"properties": {
			"auto_send_on_signup": { "type": "boolean" },
			"criteria": {
				"type": "string",
				"enum": ["any", "all"]
			},
			"error_redirect": { "type": "string" },
			"login_id_keys": {
				"type": "array",
				"items": { "$ref": "#UserVerificationKeyConfiguration" }
			}
		}
	},
	"UserVerificationKeyConfiguration": {
		"$id": "#UserVerificationKeyConfiguration",
		"type": "object",
		"properties": {
			"key": { "$ref": "#NonEmptyString" },
			"code_format": {
				"type": "string",
				"enum": ["numeric", "complex"]
			},
			"expiry": { "$ref": "#NonNegativeInteger" },
			"success_redirect": { "type": "string" },
			"error_redirect": { "type": "string" },
			"subject": { "type": "string" },
			"sender": { "type": "string" },
			"reply_to": { "type": "string" }
		}
	},
	"HookUserConfiguration": {
		"$id": "#HookUserConfiguration",
		"type": "object",
		"properties": {
			"secret": { "$ref": "#NonEmptyString" }
		},
		"required": ["secret"]
	},
	"SMTPConfiguration": {
		"$id": "#SMTPConfiguration",
		"type": "object",
		"properties": {
			"host": { "type": "string" },
			"port": { "$ref": "#NonNegativeInteger" },
			"mode": {
				"type": "string",
				"enum": ["normal", "ssl"]
			},
			"login": { "type": "string" },
			"password": { "type": "string" }
		}
	},
	"TwilioConfiguration": {
		"$id": "#TwilioConfiguration",
		"type": "object",
		"properties": {
			"account_sid": { "type": "string" },
			"auth_token": { "type": "string" },
			"from": { "type": "string" }
		}
	},
	"NexmoConfiguration": {
		"$id": "#NexmoConfiguration",
		"type": "object",
		"properties": {
			"api_key": { "type": "string" },
			"api_secret": { "type": "string" },
			"from": { "type": "string" }
		}
	}
}
`
)

var (
	userConfigurationValidator *validation.Validator
)

func init() {
	userConfigurationValidator = validation.NewValidator("http://v2.skygear.io")
	userConfigurationValidator.AddSchemaFragments(Schemas)
}

func ValidateUserConfiguration(value interface{}) error {
	return userConfigurationValidator.ValidateGoValue("#UserConfiguration", value)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/spf13/cobra"
	"github.com/tgermain/grandRepositorySky/communicator/receiver"
	"github.com/tgermain/grandRepositorySky/dht"
	"github.com/tgermain/grandRepositorySky/node"
	"github.com/tgermain/grandRepositorySky/shared"
	"github.com/tgermain/grandRepositorySky/web"
	"runtime"
	"time"
)

func MakeDHTNode(NewId *string, NewIp, NewPort string) *node.DHTnode {
	if NewId == nil || *NewId == "" {

		tempId := dht.GenerateNodeId()
		NewId = &tempId
	}
	shared.Logger.Notice("Creating node : \nId %v \nIP %s Port %.10s \n", *NewId, NewIp, NewPort)
	//Set the globally shared information
	shared.LocalId = *NewId
	shared.LocalIp = NewIp
	shared.LocalPort = NewPort

	// create node with its commInterface
	newNode, newSenderLink := node.MakeNode()

	receiverLink := receiver.MakeReceiver(newNode, newSenderLink)
	//Make the commInterface listen to incomming messages on globalIp, globalPort
	receiverLink.StartAndListen()

	return newNode
}

func main() {
	shared.SetupLogger()

	var Id string
	var Ip string
	var Port string
	var DistIp string
	var DistPort string
	var staticPath string

	rootCmd := &cobra.Command{Use: "grandRepositorySky",
		Run: func(cmd *cobra.Command, args []string) {
			node := MakeDHTNode(&Id, Ip, Port)
			node.JoinRing(&shared.DistantNode{
				"",
				DistIp,
				DistPort,
			})
			go web.MakeServer(Ip, Port, node, staticPath)
			// go func() {
			for {
				time.Sleep(time.Second)
				runtime.Gosched()
			}
			// }()
		},
	}
	rootCmd.Flags().StringVarP(&Id, "id", "n", "", "Id you want for your node")
	rootCmd.Flags().StringVarP(&Ip, "ip", "i", "", "Ip you want for your node. Localhost by default")
	rootCmd.Flags().StringVarP(&Port, "port", "p", "4321", "port you want for your node. 4321 by default")
	rootCmd.Flags().StringVarP(&DistIp, "distIp", "w", "localhost", "Ip you want for your node")
	rootCmd.Flags().StringVarP(&DistPort, "distPort", "d", "4321", "port you want for your node")
	rootCmd.Flags().StringVarP(&staticPath, "path to static ressources", "s", "web/client", "path to static ressources")
	rootCmd.Execute()
}
<commit_msg>start local web service en local host<commit_after>package main

import (
	"github.com/spf13/cobra"
	"github.com/tgermain/grandRepositorySky/communicator/receiver"
	"github.com/tgermain/grandRepositorySky/dht"
	"github.com/tgermain/grandRepositorySky/node"
	"github.com/tgermain/grandRepositorySky/shared"
	"github.com/tgermain/grandRepositorySky/web"
	"runtime"
	"time"
)

func MakeDHTNode(NewId *string, NewIp, NewPort string) *node.DHTnode {
	if NewId == nil || *NewId == "" {

		tempId := dht.GenerateNodeId()
		NewId = &tempId
	}
	shared.Logger.Notice("Creating node : \nId %v \nIP %s Port %.10s \n", *NewId, NewIp, NewPort)
	//Set the globally shared information
	shared.LocalId = *NewId
	shared.LocalIp = NewIp
	shared.LocalPort = NewPort

	// create node with its commInterface
	newNode, newSenderLink := node.MakeNode()

	receiverLink := receiver.MakeReceiver(newNode, newSenderLink)
	//Make the commInterface listen to incomming messages on globalIp, globalPort
	receiverLink.StartAndListen()

	return newNode
}

func main() {
	shared.SetupLogger()

	var Id string
	var Ip string
	var Port string
	var DistIp string
	var DistPort string
	var staticPath string

	rootCmd := &cobra.Command{Use: "grandRepositorySky",
		Run: func(cmd *cobra.Command, args []string) {
			node := MakeDHTNode(&Id, Ip, Port)
			node.JoinRing(&shared.DistantNode{
				"",
				DistIp,
				DistPort,
			})
			go web.MakeServer("", Port, node, staticPath)
			// go func() {
			for {
				time.Sleep(time.Second)
				runtime.Gosched()
			}
			// }()
		},
	}
	rootCmd.Flags().StringVarP(&Id, "id", "n", "", "Id you want for your node")
	rootCmd.Flags().StringVarP(&Ip, "ip", "i", "", "Ip you want for your node. Localhost by default")
	rootCmd.Flags().StringVarP(&Port, "port", "p", "4321", "port you want for your node. 4321 by default")
	rootCmd.Flags().StringVarP(&DistIp, "distIp", "w", "localhost", "Ip you want for your node")
	rootCmd.Flags().StringVarP(&DistPort, "distPort", "d", "4321", "port you want for your node")
	rootCmd.Flags().StringVarP(&staticPath, "path to static ressources", "s", "web/client", "path to static ressources")
	rootCmd.Execute()
}
<|endoftext|>


<commit_before>package engine

import (
	"capsulecd/pkg/config"
	"capsulecd/pkg/errors"
	"capsulecd/pkg/pipeline"
	"capsulecd/pkg/scm"
	"capsulecd/pkg/utils"
	"fmt"
	"io/ioutil"
	"path"
	"strings"
)

type genericMetadata struct {
	Version string
}
type engineGeneric struct {
	engineBase

	Scm             scm.Interface //Interface
	CurrentMetadata *genericMetadata
	NextMetadata    *genericMetadata
}

func (g *engineGeneric) Init(pipelineData *pipeline.Data, config config.Interface, sourceScm scm.Interface) error {
	g.Scm = sourceScm
	g.Config = config
	g.PipelineData = pipelineData
	g.CurrentMetadata = new(genericMetadata)
	g.NextMetadata = new(genericMetadata)

	//set command defaults (can be overridden by repo/system configuration)
	g.Config.SetDefault("engine_generic_version_path", "VERSION")
	g.Config.SetDefault("engine_cmd_compile", "echo 'skipping compile'")
	g.Config.SetDefault("engine_cmd_lint", "echo 'skipping lint'")
	g.Config.SetDefault("engine_cmd_fmt", "echo 'skipping fmt'")
	g.Config.SetDefault("engine_cmd_test", "echo 'skipping test'")
	g.Config.SetDefault("engine_cmd_security_check", "echo 'skipping security check'")
	return nil
}

func (g *engineGeneric) ValidateTools() error {
	return nil
}

func (g *engineGeneric) AssembleStep() error {
	//validate that the chef metadata.rb file exists

	if !utils.FileExists(path.Join(g.PipelineData.GitLocalPath, g.Config.GetString("engine_generic_version_path"))) {
		return errors.EngineBuildPackageInvalid(fmt.Sprintf("version file (%s) is required for metadata storage via generic engine", g.Config.GetString("engine_generic_version_path")))
	}

	// bump up the go package version
	if merr := g.retrieveCurrentMetadata(g.PipelineData.GitLocalPath); merr != nil {
		return merr
	}

	if perr := g.populateNextMetadata(); perr != nil {
		return perr
	}

	if nerr := g.writeNextMetadata(g.PipelineData.GitLocalPath); nerr != nil {
		return nerr
	}

	return nil
}

func (g *engineGeneric) DependenciesStep() error {
	return nil
}

func (g *engineGeneric) PackageStep() error {

	if cerr := utils.GitCommit(g.PipelineData.GitLocalPath, fmt.Sprintf("(v%s) Automated packaging of release by CapsuleCD", g.NextMetadata.Version)); cerr != nil {
		return cerr
	}
	tagCommit, terr := utils.GitTag(g.PipelineData.GitLocalPath, fmt.Sprintf("v%s", g.NextMetadata.Version))
	if terr != nil {
		return terr
	}

	g.PipelineData.ReleaseCommit = tagCommit
	g.PipelineData.ReleaseVersion = g.NextMetadata.Version
	return nil
}

func (g *engineGeneric) DistStep() error {
	return nil
}

//Helpers
func (g *engineGeneric) retrieveCurrentMetadata(gitLocalPath string) error {
	//read VERSION file.
	versionContent, rerr := ioutil.ReadFile(path.Join(gitLocalPath, g.Config.GetString("engine_generic_version_path")))
	if rerr != nil {
		return rerr
	}
	g.CurrentMetadata.Version = strings.TrimSpace(string(versionContent))
	return nil
}

func (g *engineGeneric) populateNextMetadata() error {

	nextVersion, err := g.BumpVersion(g.CurrentMetadata.Version)
	if err != nil {
		return err
	}

	g.NextMetadata.Version = nextVersion
	return nil
}

func (g *engineGeneric) writeNextMetadata(gitLocalPath string) error {
	return ioutil.WriteFile(path.Join(gitLocalPath, g.Config.GetString("engine_generic_version_path")), []byte(g.NextMetadata.Version), 0644)
}
<commit_msg>dynamic version template. Used for formatting version files.<commit_after>package engine

import (
	"capsulecd/pkg/config"
	"capsulecd/pkg/errors"
	"capsulecd/pkg/pipeline"
	"capsulecd/pkg/scm"
	"capsulecd/pkg/utils"
	"fmt"
	"io/ioutil"
	"path"
	"strings"
	"github.com/Masterminds/semver"
)

type genericMetadata struct {
	Version string
}
type engineGeneric struct {
	engineBase

	Scm             scm.Interface //Interface
	CurrentMetadata *genericMetadata
	NextMetadata    *genericMetadata
}

func (g *engineGeneric) Init(pipelineData *pipeline.Data, config config.Interface, sourceScm scm.Interface) error {
	g.Scm = sourceScm
	g.Config = config
	g.PipelineData = pipelineData
	g.CurrentMetadata = new(genericMetadata)
	g.NextMetadata = new(genericMetadata)

	//set command defaults (can be overridden by repo/system configuration)
	g.Config.SetDefault("engine_generic_version_template", `version := "%d.%d.%d"`)
	g.Config.SetDefault("engine_generic_version_path", "VERSION")
	g.Config.SetDefault("engine_cmd_compile", "echo 'skipping compile'")
	g.Config.SetDefault("engine_cmd_lint", "echo 'skipping lint'")
	g.Config.SetDefault("engine_cmd_fmt", "echo 'skipping fmt'")
	g.Config.SetDefault("engine_cmd_test", "echo 'skipping test'")
	g.Config.SetDefault("engine_cmd_security_check", "echo 'skipping security check'")
	return nil
}

func (g *engineGeneric) ValidateTools() error {
	return nil
}

func (g *engineGeneric) AssembleStep() error {
	//validate that the chef metadata.rb file exists

	if !utils.FileExists(path.Join(g.PipelineData.GitLocalPath, g.Config.GetString("engine_generic_version_path"))) {
		return errors.EngineBuildPackageInvalid(fmt.Sprintf("version file (%s) is required for metadata storage via generic engine", g.Config.GetString("engine_generic_version_path")))
	}

	// bump up the go package version
	if merr := g.retrieveCurrentMetadata(g.PipelineData.GitLocalPath); merr != nil {
		return merr
	}

	if perr := g.populateNextMetadata(); perr != nil {
		return perr
	}

	if nerr := g.writeNextMetadata(g.PipelineData.GitLocalPath); nerr != nil {
		return nerr
	}

	return nil
}

func (g *engineGeneric) DependenciesStep() error {
	return nil
}

func (g *engineGeneric) PackageStep() error {

	if cerr := utils.GitCommit(g.PipelineData.GitLocalPath, fmt.Sprintf("(v%s) Automated packaging of release by CapsuleCD", g.NextMetadata.Version)); cerr != nil {
		return cerr
	}
	tagCommit, terr := utils.GitTag(g.PipelineData.GitLocalPath, fmt.Sprintf("v%s", g.NextMetadata.Version))
	if terr != nil {
		return terr
	}

	g.PipelineData.ReleaseCommit = tagCommit
	g.PipelineData.ReleaseVersion = g.NextMetadata.Version
	return nil
}

func (g *engineGeneric) DistStep() error {
	return nil
}

//Helpers
func (g *engineGeneric) retrieveCurrentMetadata(gitLocalPath string) error {
	//read VERSION file.
	versionContent, rerr := ioutil.ReadFile(path.Join(gitLocalPath, g.Config.GetString("engine_generic_version_path")))
	if rerr != nil {
		return rerr
	}

	major := 0
	minor := 0
	patch := 0
	template := g.Config.GetString("engine_generic_version_template")
	fmt.Sscanf(strings.TrimSpace(string(versionContent)), template, &major, &minor, &patch)

	g.CurrentMetadata.Version = fmt.Sprintf("%d.%d.%d", major, minor, patch)
	return nil
}

func (g *engineGeneric) populateNextMetadata() error {

	nextVersion, err := g.BumpVersion(g.CurrentMetadata.Version)
	if err != nil {
		return err
	}

	g.NextMetadata.Version = nextVersion
	return nil
}

func (g *engineGeneric) writeNextMetadata(gitLocalPath string) error {

	v, nerr := semver.NewVersion(g.NextMetadata.Version)
	if nerr != nil {
		return "", nerr
	}

	template := g.Config.GetString("engine_generic_version_template")
	versionContent := fmt.Sprintf(template, v.Major(), v.Minor(), v.Patch())

	return ioutil.WriteFile(path.Join(gitLocalPath, g.Config.GetString("engine_generic_version_path")), []byte(versionContent), 0644)
}
<|endoftext|>