<commit_before>package cmd

import (
	"net/url"
	"os"

	"github.com/containerum/chkit/cmd/util"
	"github.com/containerum/chkit/pkg/chkitErrors"
	"github.com/containerum/chkit/pkg/client"
	"github.com/containerum/chkit/pkg/model"
	"gopkg.in/urfave/cli.v2"
)

const (
	ErrUnableToLoadConfig       chkitErrors.Err = "unable to load config"
	ErrInvalidUserInfo          chkitErrors.Err = "invalid user info"
	ErrInvalidAPIurl            chkitErrors.Err = "invalid API url"
	ErrUnableToLoadTokens       chkitErrors.Err = "unable to load tokens"
	ErrUnableToSaveTokens       chkitErrors.Err = "unable to save tokens"
	ErrUnableToCreateConfigDir  chkitErrors.Err = "unable to create config dir"
	ErrUnableToCreateConfigFile chkitErrors.Err = "unable to create config file"
)

func setupClient(ctx *cli.Context) error {
	log := util.GetLog(ctx)
	config := util.GetConfig(ctx)
	var client *chClient.Client
	var err error
	switch ctx.String("test") {
	case "mock":
		log.Infof("Using mock API")
		client, err = chClient.NewClient(config, chClient.WithMock)
	case "api":
		log.Infof("Using test API")
		client, err = chClient.NewClient(config, chClient.WithTestAPI)
	default:
		client, err = chClient.NewClient(config, chClient.WithCommonAPI)
	}
	if err != nil {
		return err
	}
	util.SetClient(ctx, *client)
	return nil
}

func setupConfig(ctx *cli.Context) error {
	config := util.GetConfig(ctx)
	defer util.SetConfig(ctx, config)
	log := util.GetLog(ctx)

	log.Debugf("test: %q", ctx.String("test"))
	config.Fingerprint = Fingerprint()
	tokens, err := util.LoadTokens(ctx)
	if err != nil && !os.IsNotExist(err) {
		return ErrUnableToLoadTokens.Wrap(err)
	} else if os.IsNotExist(err) {
		if err = util.SaveTokens(ctx, model.Tokens{}); err != nil {
			return ErrUnableToSaveTokens.Wrap(err)
		}
	}
	config.Tokens = tokens
	if ctx.IsSet("test") {
		testAPIurl := os.Getenv("CONTAINERUM_API")
		log.Debugf("[%s] using test api %q", util.DebugData(), testAPIurl)
		config.APIaddr = testAPIurl
	}
	if _, err := url.Parse(config.APIaddr); err != nil {
		log.Debugf("[%v] invalid API url: %q", util.DebugData(), config.APIaddr)
		return ErrInvalidAPIurl.Wrap(err)
	}
	if config.Password == "" || config.Username == "" {
		log.Debugf("[%v] invalid username or pass", util.DebugData())
		return ErrInvalidUserInfo
	}
	return nil
}

func persist(ctx *cli.Context) error {
	if !ctx.IsSet("config") {
		return util.SaveConfig(ctx)
	}
	return nil
}

func loadConfig(ctx *cli.Context) error {
	//log := util.GetLog(ctx)
	config := util.GetConfig(ctx)

	err := os.MkdirAll(util.GetConfigPath(ctx), os.ModePerm)
	if err != nil && !os.IsExist(err) {
		return ErrUnableToCreateConfigDir.Wrap(err)
	}

	_, err = os.Stat(ctx.String("config"))
	if err != nil && os.IsNotExist(err) {
		file, err := os.Create(ctx.String("config"))
		if err != nil {
			return ErrUnableToCreateConfigFile.Wrap(err)
		}
		if err = file.Close(); err != nil {
			return ErrUnableToCreateConfigDir.Wrap(err)
		}
	} else if err != nil {
		return ErrUnableToCreateConfigDir.Wrap(err)
	}

	err = util.LoadConfig(ctx.String("config"), &config)
	if err != nil {
		return ErrUnableToLoadConfig.Wrap(err)
	}
	util.SetConfig(ctx, config)
	return nil
}

func setupAll(ctx *cli.Context) error {
	log := util.GetLog(ctx)
	log.Debugf("setuping config")
	if err := loadConfig(ctx); err != nil {
		return err
	}
	if err := setupConfig(ctx); err != nil {
		return err
	}
	log.Debugf("setuping client")
	if err := setupClient(ctx); err != nil {
		return err
	}
	return nil
}
<commit_msg>rm debug info<commit_after>package cmd

import (
	"net/url"
	"os"

	"github.com/containerum/chkit/cmd/util"
	"github.com/containerum/chkit/pkg/chkitErrors"
	"github.com/containerum/chkit/pkg/client"
	"github.com/containerum/chkit/pkg/model"
	"gopkg.in/urfave/cli.v2"
)

const (
	ErrUnableToLoadConfig       chkitErrors.Err = "unable to load config"
	ErrInvalidUserInfo          chkitErrors.Err = "invalid user info"
	ErrInvalidAPIurl            chkitErrors.Err = "invalid API url"
	ErrUnableToLoadTokens       chkitErrors.Err = "unable to load tokens"
	ErrUnableToSaveTokens       chkitErrors.Err = "unable to save tokens"
	ErrUnableToCreateConfigDir  chkitErrors.Err = "unable to create config dir"
	ErrUnableToCreateConfigFile chkitErrors.Err = "unable to create config file"
)

func setupClient(ctx *cli.Context) error {
	log := util.GetLog(ctx)
	config := util.GetConfig(ctx)
	var client *chClient.Client
	var err error
	switch ctx.String("test") {
	case "mock":
		log.Infof("Using mock API")
		client, err = chClient.NewClient(config, chClient.WithMock)
	case "api":
		log.Infof("Using test API")
		client, err = chClient.NewClient(config, chClient.WithTestAPI)
	default:
		client, err = chClient.NewClient(config, chClient.WithCommonAPI)
	}
	if err != nil {
		return err
	}
	util.SetClient(ctx, *client)
	return nil
}

func setupConfig(ctx *cli.Context) error {
	config := util.GetConfig(ctx)
	defer util.SetConfig(ctx, config)
	log := util.GetLog(ctx)

	log.Debugf("test: %q", ctx.String("test"))
	config.Fingerprint = Fingerprint()
	tokens, err := util.LoadTokens(ctx)
	if err != nil && !os.IsNotExist(err) {
		return ErrUnableToLoadTokens.Wrap(err)
	} else if os.IsNotExist(err) {
		if err = util.SaveTokens(ctx, model.Tokens{}); err != nil {
			return ErrUnableToSaveTokens.Wrap(err)
		}
	}
	config.Tokens = tokens
	if ctx.IsSet("test") {
		testAPIurl := os.Getenv("CONTAINERUM_API")
		log.Debugf("using test api %q", testAPIurl)
		config.APIaddr = testAPIurl
	}
	if _, err := url.Parse(config.APIaddr); err != nil {
		log.Debugf("invalid API url: %q", config.APIaddr)
		return ErrInvalidAPIurl.Wrap(err)
	}
	if config.Password == "" || config.Username == "" {
		log.Debugf("invalid username or pass")
		return ErrInvalidUserInfo
	}
	return nil
}

func persist(ctx *cli.Context) error {
	if !ctx.IsSet("config") {
		return util.SaveConfig(ctx)
	}
	return nil
}

func loadConfig(ctx *cli.Context) error {
	//log := util.GetLog(ctx)
	config := util.GetConfig(ctx)

	err := os.MkdirAll(util.GetConfigPath(ctx), os.ModePerm)
	if err != nil && !os.IsExist(err) {
		return ErrUnableToCreateConfigDir.Wrap(err)
	}

	_, err = os.Stat(ctx.String("config"))
	if err != nil && os.IsNotExist(err) {
		file, err := os.Create(ctx.String("config"))
		if err != nil {
			return ErrUnableToCreateConfigFile.Wrap(err)
		}
		if err = file.Close(); err != nil {
			return ErrUnableToCreateConfigDir.Wrap(err)
		}
	} else if err != nil {
		return ErrUnableToCreateConfigDir.Wrap(err)
	}

	err = util.LoadConfig(ctx.String("config"), &config)
	if err != nil {
		return ErrUnableToLoadConfig.Wrap(err)
	}
	util.SetConfig(ctx, config)
	return nil
}

func setupAll(ctx *cli.Context) error {
	log := util.GetLog(ctx)
	log.Debugf("setuping config")
	if err := loadConfig(ctx); err != nil {
		return err
	}
	if err := setupConfig(ctx); err != nil {
		return err
	}
	log.Debugf("setuping client")
	if err := setupClient(ctx); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"time"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"srv-gitlab.tecnospeed.local/labs/act/lib/editor"
)

type TimeEntryStruct struct {
	IssueID    int     `json:"issue_id"`
	Date       string  `json:"spent_on"`
	Time       float64 `json:"hours"`
	ActivityID int     `json:"activity_id"`
	Comment    string  `json:"comments"`
}

type PayloadStruct struct {
	TimeEntry TimeEntryStruct `json:"time_entry"`
}

var timeEntry TimeEntryStruct

func spentRun(cmd *cobra.Command, args []string) {
	var err error

	timeEntry.IssueID = getIssueID()

	// Setting the time informed (the first arg)
	timeEntry.Time, err = strconv.ParseFloat(args[0], 64)

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

	editorPath := viper.Get("editor")
	if editorPath != nil && timeEntry.Comment == "" {
		fileName := fmt.Sprintf("%d-comment", timeEntry.IssueID)

		helperText := fmt.Sprintf("\n\n# Issue #%d\n# Date: %s\n# Time elapsed: %.2f\n# Activity ID: %d", timeEntry.IssueID, timeEntry.Date, timeEntry.Time, timeEntry.ActivityID)

		timeEntry.Comment, err = editor.Open(editorPath.(string), fileName, helperText, true)
		if err != nil {
			log.Fatal(err)
		}
	}

	if timeEntry.Comment == "" {
		log.Fatal(errors.New("Empty note"))
	}

	if timeEntry.ActivityID == 0 {
		timeEntry.ActivityID = viper.GetInt("default.activity_id")
	}

	// Validating ActivityID
	if timeEntry.ActivityID == 0 {
		log.Fatal(errors.New("activity_id is missing"))
	}

	// Sending the data to the Redmine
	payload := new(PayloadStruct)
	payload.TimeEntry = timeEntry

	marshal, err := json.Marshal(payload)

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

	url := fmt.Sprintf("http://%s/time_entries.json", viper.Get("redmine.url"))
	payloadMarshal := bytes.NewBuffer(marshal)
	request, err := http.NewRequest(http.MethodPost, url, payloadMarshal)

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

	request.Header.Add("X-Redmine-API-Key", viper.GetString("redmine.access_key"))
	request.Header.Add("Content-Type", "application/json")

	client := &http.Client{}

	response, err := client.Do(request)

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

	defer response.Body.Close()

	if response.StatusCode != http.StatusCreated {
		bodyBytes, err := ioutil.ReadAll(response.Body)

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

		log.Fatal(response.Status, "\n", string(bodyBytes))
	}

	log.Printf("Added %.2f hour(s) to the Issue #%d.", timeEntry.Time, timeEntry.IssueID)
}

// spentCmd represents the spent command
var spentCmd = &cobra.Command{
	Use:   "spent",
	Short: "Update an Issue defining the time spent on it",
	Long: `Update the Issue with the informed hours spent. The hours can be integer (ex: act spent 1) or floating point (ex: act spent 6.66).

The Activity ID can be configured with a default value (default.activity_id).

If the Date (-d) is not informed, it will use the current date.

The Issue ID can be ommited if using a regex to retrieve it from the git branch.
	`,
	Args: cobra.MinimumNArgs(1),
	Run:  spentRun,
}

func init() {
	RootCmd.AddCommand(spentCmd)

	spentCmd.Flags().IntVar(&timeEntry.ActivityID, "activity_id", 0, "The Activity ID.")

	currentDate := time.Now().Local().Format("2006-01-02")
	spentCmd.Flags().StringVarP(&timeEntry.Date, "date", "d", currentDate, "The date when the time was spent on.")
	spentCmd.Flags().StringVarP(&timeEntry.Comment, "comment", "m", "", "A short description of what was done.")
}
<commit_msg>cmd spent: able to parse in many ways the date<commit_after>// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"regexp"
	"strconv"
	"time"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"srv-gitlab.tecnospeed.local/labs/act/lib/editor"
)

// TimeEntryStruct is expected format of the Redmine API
type TimeEntryStruct struct {
	IssueID    int     `json:"issue_id"`
	Date       string  `json:"spent_on"`
	Time       float64 `json:"hours"`
	ActivityID int     `json:"activity_id"`
	Comment    string  `json:"comments"`
}

// PayloadStruct is the envelope to the time_entry expected by the Redmine API
type PayloadStruct struct {
	TimeEntry TimeEntryStruct `json:"time_entry"`
}

var timeEntry TimeEntryStruct

func parseMonthDay(input string) (output string, err error) {
	regexDayAndMonth := regexp.MustCompile(`^(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$`)
	dayAndMonth := regexDayAndMonth.FindStringSubmatch(input)

	if len(dayAndMonth) == 0 {
		return
	}

	day, err := strconv.Atoi(dayAndMonth[2])

	if err != nil {
		return
	}

	month, err := strconv.Atoi(dayAndMonth[1])

	if err != nil {
		return
	}

	timeNow := time.Now().Local()
	monthDayDate := time.Date(timeNow.Year(), time.Month(month), day, 0, 0, 0, 0, timeNow.Location())
	output = monthDayDate.Format("2006-01-02")
	return
}

func parseRetroactiveDate(input string) (output string, err error) {
	retroactive, err := regexp.MatchString("^-[0-9]*$", input)

	if err != nil || !retroactive {
		return
	}

	daysToBack, err := strconv.Atoi(input)

	if err != nil {
		return
	}

	timeNow := time.Now().Local()
	output = timeNow.AddDate(0, 0, daysToBack).Format("2006-01-02")
	return

}

func parseDate(input string) (output string, err error) {
	complete, err := regexp.MatchString(`^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))$`, input)

	if err != nil {
		return
	}

	if complete {
		output = input
		return
	}

	monthDay, err := parseMonthDay(input)

	if err != nil {
		return
	}

	if monthDay != "" {
		output = monthDay
		return
	}

	retroactiveDate, err := parseRetroactiveDate(input)

	if err != nil {
		return
	}

	if retroactiveDate != "" {
		output = retroactiveDate
		return
	}

	// look for a number (only the day). ex: 2
	day, err := strconv.Atoi(input)

	if err != nil {
		return
	}

	timeNow := time.Now().Local()
	output = time.Date(timeNow.Year(), timeNow.Month(), day, 0, 0, 0, 0, timeNow.Location()).Format("2006-01-02")
	return
}

func spentRun(cmd *cobra.Command, args []string) {
	var err error

	timeEntry.IssueID = getIssueID()

	// Setting the time informed (the first arg)
	timeEntry.Time, err = strconv.ParseFloat(args[0], 64)

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

	timeEntry.Date, err = parseDate(timeEntry.Date)

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

	editorPath := viper.Get("editor")
	if editorPath != nil && timeEntry.Comment == "" {
		fileName := fmt.Sprintf("%d-comment", timeEntry.IssueID)

		helperText := fmt.Sprintf("\n\n# Issue #%d\n# Date: %s\n# Time elapsed: %.2f\n# Activity ID: %d", timeEntry.IssueID, timeEntry.Date, timeEntry.Time, timeEntry.ActivityID)

		timeEntry.Comment, err = editor.Open(editorPath.(string), fileName, helperText, true)
		if err != nil {
			log.Fatal(err)
		}
	}

	if timeEntry.Comment == "" {
		log.Fatal(errors.New("Empty note"))
	}

	if timeEntry.ActivityID == 0 {
		timeEntry.ActivityID = viper.GetInt("default.activity_id")
	}

	// Validating ActivityID
	if timeEntry.ActivityID == 0 {
		log.Fatal(errors.New("activity_id is missing"))
	}

	// Sending the data to the Redmine
	payload := new(PayloadStruct)
	payload.TimeEntry = timeEntry

	marshal, err := json.Marshal(payload)

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

	url := fmt.Sprintf("http://%s/time_entries.json", viper.Get("redmine.url"))
	payloadMarshal := bytes.NewBuffer(marshal)
	request, err := http.NewRequest(http.MethodPost, url, payloadMarshal)

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

	request.Header.Add("X-Redmine-API-Key", viper.GetString("redmine.access_key"))
	request.Header.Add("Content-Type", "application/json")

	client := &http.Client{}

	response, err := client.Do(request)

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

	defer response.Body.Close()

	if response.StatusCode != http.StatusCreated {
		bodyBytes, err := ioutil.ReadAll(response.Body)

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

		log.Fatal(response.Status, "\n", string(bodyBytes))
	}

	log.Printf("Added %.2f hour(s) to the Issue #%d.", timeEntry.Time, timeEntry.IssueID)
}

// spentCmd represents the spent command
var spentCmd = &cobra.Command{
	Use:   "spent",
	Short: "Update an Issue defining the time spent on it",
	Long: `Update the Issue with the informed hours spent. The hours can be integer (ex: act spent 1) or floating point (ex: act spent 6.66).

The Activity ID can be configured with a default value (default.activity_id).

The Date can be informed as:
-d 2017-09-22 -- Complete
-d      09-22 -- Only the month and day. The year will be the current one.
-d         22 -- Only the day. The year and month will be the current ones.
-d         -1 -- Informing how many days back from the current date.
And if not informed, it will use the current date.

The Issue ID can be ommited if using a regex to retrieve it from the git branch.
	`,
	Args: cobra.MinimumNArgs(1),
	Run:  spentRun,
}

func init() {
	RootCmd.AddCommand(spentCmd)

	spentCmd.Flags().IntVar(&timeEntry.ActivityID, "activity_id", 0, "The Activity ID.")

	currentDate := time.Now().Local().Format("2006-01-02")
	spentCmd.Flags().StringVarP(&timeEntry.Date, "date", "d", currentDate, "The date when the time was spent on.")
	spentCmd.Flags().StringVarP(&timeEntry.Comment, "comment", "m", "", "A short description of what was done.")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"sort"
	"time"

	humanize "github.com/dustin/go-humanize"

	"github.com/yomon8/aloget/config"
	"github.com/yomon8/aloget/downloader"
	"github.com/yomon8/aloget/list"
)

const (
	timeFormatInput = "2006-01-02 15:04:05 MST"
)

func main() {
	cfg, err := config.LoadConfig()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	zone := "UTC"
	if !cfg.IsUTC {
		zone, _ = time.Now().In(time.Local).Zone()
	}

	start, _ := time.Parse(
		timeFormatInput,
		fmt.Sprintf("%s %s", cfg.StartTime, zone),
	)

	end, _ := time.Parse(
		timeFormatInput,
		fmt.Sprintf("%s %s", cfg.EndTime, zone),
	)

	list, err := list.GetObjectList(start, end, cfg)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	if list.Len() == 0 {
		fmt.Println("No S3 objects selected, maybe invalid values in parameters")
		os.Exit(1)
	}

	totalSizeBytes := list.GetTotalByte()
	sort.Sort(list)

	// wait for user prompt
	if !cfg.ForceMode {
		var key string
		var ok bool
		for !ok {
			fmt.Printf("%s %s  -  %s\n",
				fmt.Sprintf("From-To(%s) \t:", zone),
				list.GetOldestTime().In(time.Local).Format(timeFormatInput),
				list.GetLatestTime().In(time.Local).Format(timeFormatInput),
			)
			fmt.Printf("%s %s  -  %s\n",
				"From-To(UTC)   \t:",
				list.GetOldestTime().Format(timeFormatInput),
				list.GetLatestTime().Format(timeFormatInput),
			)
			fmt.Printf("%s %s\n",
				"Download Size  \t:",
				humanize.Bytes(uint64(totalSizeBytes)),
			)
			fmt.Printf("%s %s\n",
				"Decompress Gzip\t:",
				fmt.Sprint(!cfg.NoDecompress),
			)
			fmt.Printf("%s %d objects\n",
				"S3 Objects    \t:",
				list.Len(),
			)
			fmt.Print("Start/Cancel>")
			fmt.Scanf("%s", &key)
			switch key {
			case "S", "s", "Start", "start":
				ok = true
			case "C", "c", "Cancel", "cancel":
				fmt.Println("canceled.")
				os.Exit(1)
			default:
				continue
			}
		}
	}

	err = downloader.NewDownloader(cfg).Download(list)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	fmt.Printf("Download Completed.\n")
}
<commit_msg>fix prompt message<commit_after>package main

import (
	"fmt"
	"os"
	"sort"
	"time"

	humanize "github.com/dustin/go-humanize"

	"github.com/yomon8/aloget/config"
	"github.com/yomon8/aloget/downloader"
	"github.com/yomon8/aloget/list"
)

const (
	timeFormatInput = "2006-01-02 15:04:05 MST"
)

func main() {
	cfg, err := config.LoadConfig()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	zone := "UTC"
	if !cfg.IsUTC {
		zone, _ = time.Now().In(time.Local).Zone()
	}

	start, _ := time.Parse(
		timeFormatInput,
		fmt.Sprintf("%s %s", cfg.StartTime, zone),
	)

	end, _ := time.Parse(
		timeFormatInput,
		fmt.Sprintf("%s %s", cfg.EndTime, zone),
	)

	list, err := list.GetObjectList(start, end, cfg)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	if list.Len() == 0 {
		fmt.Println("No S3 objects selected, maybe invalid values in parameters")
		os.Exit(1)
	}

	totalSizeBytes := list.GetTotalByte()
	sort.Sort(list)

	// wait for user prompt
	if !cfg.ForceMode {
		var key string
		var ok bool
		for !ok {
			fmt.Printf("%s %s  -  %s\n",
				"From-To(Local) \t:",
				list.GetOldestTime().In(time.Local).Format(timeFormatInput),
				list.GetLatestTime().In(time.Local).Format(timeFormatInput),
			)
			fmt.Printf("%s %s  -  %s\n",
				"From-To(UTC)   \t:",
				list.GetOldestTime().Format(timeFormatInput),
				list.GetLatestTime().Format(timeFormatInput),
			)
			fmt.Printf("%s %s\n",
				"Download Size  \t:",
				humanize.Bytes(uint64(totalSizeBytes)),
			)
			fmt.Printf("%s %s\n",
				"Decompress Gzip\t:",
				fmt.Sprint(!cfg.NoDecompress),
			)
			fmt.Printf("%s %d objects\n",
				"S3 Objects    \t:",
				list.Len(),
			)
			fmt.Print("Start/Cancel>")
			fmt.Scanf("%s", &key)
			switch key {
			case "S", "s", "Start", "start":
				ok = true
			case "C", "c", "Cancel", "cancel":
				fmt.Println("canceled.")
				os.Exit(1)
			default:
				continue
			}
		}
	}

	err = downloader.NewDownloader(cfg).Download(list)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	fmt.Printf("Download Completed.\n")
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: MIT
//
// Copyright © 2019 Kent Gibson <warthog618@gmail.com>.

// +build linux

package main

import (
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/spf13/cobra"
	"github.com/warthog618/gpio"
)

var rootCmd = &cobra.Command{
	Use:   "gppiio",
	Short: "gppiio is a utility to control Raspberry Pi GPIO pins",
	Run: func(cmd *cobra.Command, args []string) {
		cmd.Help()
	},
	Version: version,
}

func main() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func logErr(cmd *cobra.Command, err error) {
	fmt.Fprintf(os.Stderr, "gppiio %s: %s\n", cmd.Name(), err)
}

var pinNames = map[string]int{
	"J8P3":  gpio.J8p3,
	"J8P03": gpio.J8p3,
	"J8P5":  gpio.J8p5,
	"J8P05": gpio.J8p5,
	"J8P7":  gpio.J8p7,
	"J8P07": gpio.J8p7,
	"J8P8":  gpio.J8p8,
	"J8P08": gpio.J8p8,
	"J8P10": gpio.J8p10,
	"J8P11": gpio.J8p11,
	"J8P12": gpio.J8p12,
	"J8P13": gpio.J8p12,
	"J8P15": gpio.J8p15,
	"J8P16": gpio.J8p16,
	"J8P18": gpio.J8p18,
	"J8P19": gpio.J8p19,
	"J8P21": gpio.J8p21,
	"J8P22": gpio.J8p22,
	"J8P23": gpio.J8p23,
	"J8P24": gpio.J8p24,
	"J8P26": gpio.J8p26,
	"J8P27": gpio.J8p27,
	"J8P28": gpio.J8p28,
	"J8P29": gpio.J8p29,
	"J8P31": gpio.J8p31,
	"J8P32": gpio.J8p32,
	"J8P33": gpio.J8p33,
	"J8P35": gpio.J8p35,
	"J8P36": gpio.J8p36,
	"J8P37": gpio.J8p37,
	"J8P38": gpio.J8p38,
	"J8P40": gpio.J8p40,
}

func parseOffset(arg string) (int, error) {
	if o, ok := pinNames[strings.ToUpper(arg)]; ok {
		return o, nil
	}
	o, err := strconv.ParseUint(arg, 10, 64)
	if err != nil {
		return 0, fmt.Errorf("can't parse pin '%s'", arg)
	}
	if o >= gpio.MaxGPIOPin {
		return 0, fmt.Errorf("unknown pin '%d'", o)
	}
	return int(o), nil
}
<commit_msg>disable --version<commit_after>// SPDX-License-Identifier: MIT
//
// Copyright © 2019 Kent Gibson <warthog618@gmail.com>.

// +build linux

package main

import (
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/spf13/cobra"
	"github.com/warthog618/gpio"
)

var rootCmd = &cobra.Command{
	Use:   "gppiio",
	Short: "gppiio is a utility to control Raspberry Pi GPIO pins",
	Run: func(cmd *cobra.Command, args []string) {
		cmd.Help()
	},
}

func main() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func logErr(cmd *cobra.Command, err error) {
	fmt.Fprintf(os.Stderr, "gppiio %s: %s\n", cmd.Name(), err)
}

var pinNames = map[string]int{
	"J8P3":  gpio.J8p3,
	"J8P03": gpio.J8p3,
	"J8P5":  gpio.J8p5,
	"J8P05": gpio.J8p5,
	"J8P7":  gpio.J8p7,
	"J8P07": gpio.J8p7,
	"J8P8":  gpio.J8p8,
	"J8P08": gpio.J8p8,
	"J8P10": gpio.J8p10,
	"J8P11": gpio.J8p11,
	"J8P12": gpio.J8p12,
	"J8P13": gpio.J8p12,
	"J8P15": gpio.J8p15,
	"J8P16": gpio.J8p16,
	"J8P18": gpio.J8p18,
	"J8P19": gpio.J8p19,
	"J8P21": gpio.J8p21,
	"J8P22": gpio.J8p22,
	"J8P23": gpio.J8p23,
	"J8P24": gpio.J8p24,
	"J8P26": gpio.J8p26,
	"J8P27": gpio.J8p27,
	"J8P28": gpio.J8p28,
	"J8P29": gpio.J8p29,
	"J8P31": gpio.J8p31,
	"J8P32": gpio.J8p32,
	"J8P33": gpio.J8p33,
	"J8P35": gpio.J8p35,
	"J8P36": gpio.J8p36,
	"J8P37": gpio.J8p37,
	"J8P38": gpio.J8p38,
	"J8P40": gpio.J8p40,
}

func parseOffset(arg string) (int, error) {
	if o, ok := pinNames[strings.ToUpper(arg)]; ok {
		return o, nil
	}
	o, err := strconv.ParseUint(arg, 10, 64)
	if err != nil {
		return 0, fmt.Errorf("can't parse pin '%s'", arg)
	}
	if o >= gpio.MaxGPIOPin {
		return 0, fmt.Errorf("unknown pin '%d'", o)
	}
	return int(o), nil
}
<|endoftext|>


<commit_before>package main

import (
	"expvar"
	"flag"
	"fmt"
	"github.com/valyala/fasthttp"
	"github.com/valyala/fasthttp/expvarhandler"
	"log"
	"net"
	"sync/atomic"
)

var (
	expvarAddr = flag.String("expvarAddr", "localhost:8040", "TCP address for exporting httptp metrics. They are exported "+
		"at the following pages:\n"+
		"\thttp://expvarAddr/expvar - in expvar format\n"+
		"\thttp://expvarAddr/prometheus - in prometheus format\n")
)

func initExpvarServer() {
	if *expvarAddr == "" {
		return
	}

	log.Printf("exporting stats at http://%[1]s/expvar and http://%[1]s/prometheus", *expvarAddr)

	go func() {
		if err := fasthttp.ListenAndServe(*expvarAddr, expvarHandler); err != nil {
			log.Fatalf("error in expvar server: %s", err)
		}
	}()
}

func expvarHandler(ctx *fasthttp.RequestCtx) {
	path := ctx.Path()
	switch string(path) {
	case "/expvar":
		expvarhandler.ExpvarHandler(ctx)
	case "/prometheus":
		prometheusHandler(ctx)
	default:
		ctx.Error("unsupported path", fasthttp.StatusBadRequest)
	}
}

func prometheusHandler(ctx *fasthttp.RequestCtx) {
	prometheusHandlerCalls.Add(1)
	expvar.Do(func(kv expvar.KeyValue) {
		if x, ok := kv.Value.(*expvar.Int); ok {
			fmt.Fprintf(ctx, "# TYPE %s counter\n", kv.Key)
			fmt.Fprintf(ctx, "%s %s\n", kv.Key, x)
		}
	})
}

func newExpvarDial(dial fasthttp.DialFunc) fasthttp.DialFunc {
	return func(addr string) (net.Conn, error) {
		conn, err := dial(addr)
		if err != nil {
			outDialError.Add(1)
			return nil, err
		}
		outConns.Add(1)
		outDialSuccess.Add(1)
		return &expvarConn{
			Conn: conn,

			conns:        outConns,
			bytesWritten: outBytesWritten,
			bytesRead:    outBytesRead,
			writeError:   outWriteError,
			readError:    outReadError,
			writeCalls:   outWriteCalls,
			readCalls:    outReadCalls,
		}, nil
	}
}

type expvarConn struct {
	net.Conn

	conns        *expvar.Int
	bytesWritten *expvar.Int
	bytesRead    *expvar.Int
	writeError   *expvar.Int
	readError    *expvar.Int
	writeCalls   *expvar.Int
	readCalls    *expvar.Int

	closed uint32
}

func (c *expvarConn) Close() error {
	if atomic.AddUint32(&c.closed, 1) == 1 {
		c.conns.Add(-1)
	}
	return c.Conn.Close()
}

func (c *expvarConn) Write(p []byte) (int, error) {
	n, err := c.Conn.Write(p)
	c.writeCalls.Add(1)
	c.bytesWritten.Add(int64(n))
	if err != nil {
		c.writeError.Add(1)
	}
	return n, err
}

func (c *expvarConn) Read(p []byte) (int, error) {
	n, err := c.Conn.Read(p)
	c.readCalls.Add(1)
	c.bytesRead.Add(int64(n))
	if err != nil {
		c.readError.Add(1)
	}
	return n, err
}

var (
	outDialSuccess  = expvar.NewInt("outDialSuccess")
	outDialError    = expvar.NewInt("outDialError")
	outConns        = expvar.NewInt("outConns")
	outBytesWritten = expvar.NewInt("outBytesWritten")
	outBytesRead    = expvar.NewInt("outBytesRead")
	outWriteError   = expvar.NewInt("outWriteError")
	outReadError    = expvar.NewInt("outReadError")
	outWriteCalls   = expvar.NewInt("outWriteCalls")
	outReadCalls    = expvar.NewInt("outReadCalls")

	prometheusHandlerCalls = expvar.NewInt("prometheusHandlerCalls")
)

type expvarListener struct {
	net.Listener
}

func (ln *expvarListener) Accept() (net.Conn, error) {
	conn, err := ln.Listener.Accept()
	if err != nil {
		inAcceptError.Add(1)
		return nil, err
	}
	inAcceptSuccess.Add(1)
	inConns.Add(1)
	return &expvarConn{
		Conn: conn,

		conns:        inConns,
		bytesWritten: inBytesWritten,
		bytesRead:    inBytesRead,
		writeError:   inWriteError,
		readError:    inReadError,
		writeCalls:   inWriteCalls,
		readCalls:    inReadCalls,
	}, nil
}

var (
	inAcceptSuccess = expvar.NewInt("inAcceptSuccess")
	inAcceptError   = expvar.NewInt("inAcceptError")
	inConns         = expvar.NewInt("inConns")
	inBytesWritten  = expvar.NewInt("inBytesWritten")
	inBytesRead     = expvar.NewInt("inBytesRead")
	inWriteError    = expvar.NewInt("inWriteError")
	inReadError     = expvar.NewInt("inReadError")
	inWriteCalls    = expvar.NewInt("inWriteCalls")
	inReadCalls     = expvar.NewInt("inReadCalls")
)
<commit_msg>cmd/httptp: do not count io.EOF in inReadError<commit_after>package main

import (
	"expvar"
	"flag"
	"fmt"
	"github.com/valyala/fasthttp"
	"github.com/valyala/fasthttp/expvarhandler"
	"io"
	"log"
	"net"
	"sync/atomic"
)

var (
	expvarAddr = flag.String("expvarAddr", "localhost:8040", "TCP address for exporting httptp metrics. They are exported "+
		"at the following pages:\n"+
		"\thttp://expvarAddr/expvar - in expvar format\n"+
		"\thttp://expvarAddr/prometheus - in prometheus format\n")
)

func initExpvarServer() {
	if *expvarAddr == "" {
		return
	}

	log.Printf("exporting stats at http://%[1]s/expvar and http://%[1]s/prometheus", *expvarAddr)

	go func() {
		if err := fasthttp.ListenAndServe(*expvarAddr, expvarHandler); err != nil {
			log.Fatalf("error in expvar server: %s", err)
		}
	}()
}

func expvarHandler(ctx *fasthttp.RequestCtx) {
	path := ctx.Path()
	switch string(path) {
	case "/expvar":
		expvarhandler.ExpvarHandler(ctx)
	case "/prometheus":
		prometheusHandler(ctx)
	default:
		ctx.Error("unsupported path", fasthttp.StatusBadRequest)
	}
}

func prometheusHandler(ctx *fasthttp.RequestCtx) {
	prometheusHandlerCalls.Add(1)
	expvar.Do(func(kv expvar.KeyValue) {
		if x, ok := kv.Value.(*expvar.Int); ok {
			fmt.Fprintf(ctx, "# TYPE %s counter\n", kv.Key)
			fmt.Fprintf(ctx, "%s %s\n", kv.Key, x)
		}
	})
}

func newExpvarDial(dial fasthttp.DialFunc) fasthttp.DialFunc {
	return func(addr string) (net.Conn, error) {
		conn, err := dial(addr)
		if err != nil {
			outDialError.Add(1)
			return nil, err
		}
		outConns.Add(1)
		outDialSuccess.Add(1)
		return &expvarConn{
			Conn: conn,

			conns:        outConns,
			bytesWritten: outBytesWritten,
			bytesRead:    outBytesRead,
			writeError:   outWriteError,
			readError:    outReadError,
			writeCalls:   outWriteCalls,
			readCalls:    outReadCalls,
		}, nil
	}
}

type expvarConn struct {
	net.Conn

	conns        *expvar.Int
	bytesWritten *expvar.Int
	bytesRead    *expvar.Int
	writeError   *expvar.Int
	readError    *expvar.Int
	writeCalls   *expvar.Int
	readCalls    *expvar.Int

	closed uint32
}

func (c *expvarConn) Close() error {
	if atomic.AddUint32(&c.closed, 1) == 1 {
		c.conns.Add(-1)
	}
	return c.Conn.Close()
}

func (c *expvarConn) Write(p []byte) (int, error) {
	n, err := c.Conn.Write(p)
	c.writeCalls.Add(1)
	c.bytesWritten.Add(int64(n))
	if err != nil {
		c.writeError.Add(1)
	}
	return n, err
}

func (c *expvarConn) Read(p []byte) (int, error) {
	n, err := c.Conn.Read(p)
	c.readCalls.Add(1)
	c.bytesRead.Add(int64(n))
	if err != nil && err != io.EOF {
		c.readError.Add(1)
	}
	return n, err
}

var (
	outDialSuccess  = expvar.NewInt("outDialSuccess")
	outDialError    = expvar.NewInt("outDialError")
	outConns        = expvar.NewInt("outConns")
	outBytesWritten = expvar.NewInt("outBytesWritten")
	outBytesRead    = expvar.NewInt("outBytesRead")
	outWriteError   = expvar.NewInt("outWriteError")
	outReadError    = expvar.NewInt("outReadError")
	outWriteCalls   = expvar.NewInt("outWriteCalls")
	outReadCalls    = expvar.NewInt("outReadCalls")

	prometheusHandlerCalls = expvar.NewInt("prometheusHandlerCalls")
)

type expvarListener struct {
	net.Listener
}

func (ln *expvarListener) Accept() (net.Conn, error) {
	conn, err := ln.Listener.Accept()
	if err != nil {
		inAcceptError.Add(1)
		return nil, err
	}
	inAcceptSuccess.Add(1)
	inConns.Add(1)
	return &expvarConn{
		Conn: conn,

		conns:        inConns,
		bytesWritten: inBytesWritten,
		bytesRead:    inBytesRead,
		writeError:   inWriteError,
		readError:    inReadError,
		writeCalls:   inWriteCalls,
		readCalls:    inReadCalls,
	}, nil
}

var (
	inAcceptSuccess = expvar.NewInt("inAcceptSuccess")
	inAcceptError   = expvar.NewInt("inAcceptError")
	inConns         = expvar.NewInt("inConns")
	inBytesWritten  = expvar.NewInt("inBytesWritten")
	inBytesRead     = expvar.NewInt("inBytesRead")
	inWriteError    = expvar.NewInt("inWriteError")
	inReadError     = expvar.NewInt("inReadError")
	inWriteCalls    = expvar.NewInt("inWriteCalls")
	inReadCalls     = expvar.NewInt("inReadCalls")
)
<|endoftext|>


<commit_before>package auth

import (
	"crypto/subtle"
	"fmt"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"golang.org/x/net/context"
)

type digestClient struct {
	nc       uint64
	lastSeen int64
}

// DigestAuth is an authenticator implementation for 'Digest' HTTP Authentication scheme (RFC 7616).
//
// Note: this implementation was written following now deprecated RFC
// 2617, and supports only MD5 algorithm.
//
// TODO: Add support for SHA-256 and SHA-512/256 algorithms.
type DigestAuth struct {
	Realm            string
	Opaque           string
	Secrets          SecretProvider
	PlainTextSecrets bool
	IgnoreNonceCount bool
	// Headers used by authenticator. Set to ProxyHeaders to use with
	// proxy server. When nil, NormalHeaders are used.
	Headers *Headers

	/*
	   Approximate size of Client's Cache. When actual number of
	   tracked client nonces exceeds
	   ClientCacheSize+ClientCacheTolerance, ClientCacheTolerance*2
	   older entries are purged.
	*/
	ClientCacheSize      int
	ClientCacheTolerance int

	clients map[string]*digestClient
	mutex   sync.RWMutex
}

// check that DigestAuth implements AuthenticatorInterface
var _ = (AuthenticatorInterface)((*DigestAuth)(nil))

type digestCacheEntry struct {
	nonce    string
	lastSeen int64
}

type digestCache []digestCacheEntry

func (c digestCache) Less(i, j int) bool {
	return c[i].lastSeen < c[j].lastSeen
}

func (c digestCache) Len() int {
	return len(c)
}

func (c digestCache) Swap(i, j int) {
	c[i], c[j] = c[j], c[i]
}

// Purge removes count oldest entries from DigestAuth.clients
func (da *DigestAuth) Purge(count int) {
	da.mutex.Lock()
	defer da.mutex.Unlock()
	entries := make([]digestCacheEntry, 0, len(da.clients))
	for nonce, client := range da.clients {
		entries = append(entries, digestCacheEntry{nonce, client.lastSeen})
	}
	cache := digestCache(entries)
	sort.Sort(cache)
	for _, client := range cache[:count] {
		delete(da.clients, client.nonce)
	}
}

// RequireAuth is an http.HandlerFunc which initiates the
// authentication process (or requires reauthentication).
func (da *DigestAuth) RequireAuth(w http.ResponseWriter, r *http.Request) {
	da.mutex.RLock()
	clientsLen := len(da.clients)
	da.mutex.RUnlock()

	if clientsLen > da.ClientCacheSize+da.ClientCacheTolerance {
		da.Purge(da.ClientCacheTolerance * 2)
	}
	nonce := RandomKey()

	da.mutex.Lock()
	da.clients[nonce] = &digestClient{nc: 0, lastSeen: time.Now().UnixNano()}
	da.mutex.Unlock()

	da.mutex.RLock()
	w.Header().Set(contentType, da.Headers.V().UnauthContentType)
	w.Header().Set(da.Headers.V().Authenticate,
		fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm=MD5, qop="auth"`,
			da.Realm, nonce, da.Opaque))
	w.WriteHeader(da.Headers.V().UnauthCode)
	w.Write([]byte(da.Headers.V().UnauthResponse))
	da.mutex.RUnlock()
}

// DigestAuthParams parses Authorization header from the
// http.Request. Returns a map of auth parameters or nil if the header
// is not a valid parsable Digest auth header.
func DigestAuthParams(authorization string) map[string]string {
	s := strings.SplitN(authorization, " ", 2)
	if len(s) != 2 || s[0] != "Digest" {
		return nil
	}

	return ParsePairs(s[1])
}

// CheckAuth checks whether the request contains valid authentication
// data. Returns a pair of username, authinfo, where username is the
// name of the authenticated user or an empty string and authinfo is
// the contents for the optional Authentication-Info response header.
func (da *DigestAuth) CheckAuth(r *http.Request) (username string, authinfo *string) {
	da.mutex.RLock()
	defer da.mutex.RUnlock()
	username = ""
	authinfo = nil
	auth := DigestAuthParams(r.Header.Get(da.Headers.V().Authorization))
	if auth == nil {
		return "", nil
	}
	// RFC2617 Section 3.2.1 specifies that unset value of algorithm in
	// WWW-Authenticate Response header should be treated as
	// "MD5". According to section 3.2.2 the "algorithm" value in
	// subsequent Request Authorization header must be set to whatever
	// was supplied in the WWW-Authenticate Response header. This
	// implementation always returns an algorithm in WWW-Authenticate
	// header, however there seems to be broken clients in the wild
	// which do not set the algorithm. Assume the unset algorithm in
	// Authorization header to be equal to MD5.
	if _, ok := auth["algorithm"]; !ok {
		auth["algorithm"] = "MD5"
	}
	if da.Opaque != auth["opaque"] || auth["algorithm"] != "MD5" || auth["qop"] != "auth" {
		return "", nil
	}

	// Check if the requested URI matches auth header
	if r.RequestURI != auth["uri"] {
		// We allow auth["uri"] to be a full path prefix of request-uri
		// for some reason lost in history, which is probably wrong, but
		// used to be like that for quite some time
		// (https://tools.ietf.org/html/rfc2617#section-3.2.2 explicitly
		// says that auth["uri"] is the request-uri).
		//
		// TODO: make an option to allow only strict checking.
		switch u, err := url.Parse(auth["uri"]); {
		case err != nil:
			return "", nil
		case r.URL == nil:
			return "", nil
		case len(u.Path) > len(r.URL.Path):
			return "", nil
		case !strings.HasPrefix(r.URL.Path, u.Path):
			return "", nil
		}
	}

	HA1 := da.Secrets(auth["username"], da.Realm)
	if da.PlainTextSecrets {
		HA1 = H(auth["username"] + ":" + da.Realm + ":" + HA1)
	}
	HA2 := H(r.Method + ":" + auth["uri"])
	KD := H(strings.Join([]string{HA1, auth["nonce"], auth["nc"], auth["cnonce"], auth["qop"], HA2}, ":"))

	if subtle.ConstantTimeCompare([]byte(KD), []byte(auth["response"])) != 1 {
		return "", nil
	}

	// At this point crypto checks are completed and validated.
	// Now check if the session is valid.

	nc, err := strconv.ParseUint(auth["nc"], 16, 64)
	if err != nil {
		return "", nil
	}

	client, ok := da.clients[auth["nonce"]]
	if !ok {
		return "", nil
	}
	if client.nc != 0 && client.nc >= nc && !da.IgnoreNonceCount {
		return "", nil
	}
	client.nc = nc
	client.lastSeen = time.Now().UnixNano()

	respHA2 := H(":" + auth["uri"])
	rspauth := H(strings.Join([]string{HA1, auth["nonce"], auth["nc"], auth["cnonce"], auth["qop"], respHA2}, ":"))

	info := fmt.Sprintf(`qop="auth", rspauth="%s", cnonce="%s", nc="%s"`, rspauth, auth["cnonce"], auth["nc"])
	return auth["username"], &info
}

// Default values for ClientCacheSize and ClientCacheTolerance for DigestAuth
const (
	DefaultClientCacheSize      = 1000
	DefaultClientCacheTolerance = 100
)

// Wrap returns an http.HandlerFunc wraps AuthenticatedHandlerFunc
// with this DigestAuth authentication checks. Once the request
// contains valid credentials, it calls wrapped
// AuthenticatedHandlerFunc.
//
// Deprecated: new code should use NewContext instead.
func (da *DigestAuth) Wrap(wrapped AuthenticatedHandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if username, authinfo := da.CheckAuth(r); username == "" {
			da.RequireAuth(w, r)
		} else {
			ar := &AuthenticatedRequest{Request: *r, Username: username}
			if authinfo != nil {
				w.Header().Set(da.Headers.V().AuthInfo, *authinfo)
			}
			wrapped(w, ar)
		}
	}
}

// JustCheck returns a new http.HandlerFunc, which requires
// DigestAuth to successfully authenticate a user before calling
// wrapped http.HandlerFunc.
//
// Authenticated Username is passed as an extra
// X-Authenticated-Username header to the wrapped HandlerFunc.
func (da *DigestAuth) JustCheck(wrapped http.HandlerFunc) http.HandlerFunc {
	return da.Wrap(func(w http.ResponseWriter, ar *AuthenticatedRequest) {
		ar.Header.Set(AuthUsernameHeader, ar.Username)
		wrapped(w, &ar.Request)
	})
}

// NewContext returns a context carrying authentication information for the request.
func (da *DigestAuth) NewContext(ctx context.Context, r *http.Request) context.Context {
	da.mutex.Lock()
	defer da.mutex.Unlock()
	username, authinfo := da.CheckAuth(r)
	info := &Info{Username: username, ResponseHeaders: make(http.Header)}
	if username != "" {
		info.Authenticated = true
		info.ResponseHeaders.Set(da.Headers.V().AuthInfo, *authinfo)
	} else {
		// return back digest WWW-Authenticate header
		if len(da.clients) > da.ClientCacheSize+da.ClientCacheTolerance {
			da.Purge(da.ClientCacheTolerance * 2)
		}
		nonce := RandomKey()
		da.clients[nonce] = &digestClient{nc: 0, lastSeen: time.Now().UnixNano()}
		info.ResponseHeaders.Set(da.Headers.V().Authenticate,
			fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm=MD5, qop="auth"`,
				da.Realm, nonce, da.Opaque))
	}
	return context.WithValue(ctx, infoKey, info)
}

// NewDigestAuthenticator generates a new DigestAuth object
func NewDigestAuthenticator(realm string, secrets SecretProvider) *DigestAuth {
	da := &DigestAuth{
		Opaque:               RandomKey(),
		Realm:                realm,
		Secrets:              secrets,
		PlainTextSecrets:     false,
		ClientCacheSize:      DefaultClientCacheSize,
		ClientCacheTolerance: DefaultClientCacheTolerance,
		clients:              map[string]*digestClient{}}
	return da
}
<commit_msg>Fix deadlock in NewContext introduced in 3da094f1.<commit_after>package auth

import (
	"crypto/subtle"
	"fmt"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"golang.org/x/net/context"
)

type digestClient struct {
	nc       uint64
	lastSeen int64
}

// DigestAuth is an authenticator implementation for 'Digest' HTTP Authentication scheme (RFC 7616).
//
// Note: this implementation was written following now deprecated RFC
// 2617, and supports only MD5 algorithm.
//
// TODO: Add support for SHA-256 and SHA-512/256 algorithms.
type DigestAuth struct {
	Realm            string
	Opaque           string
	Secrets          SecretProvider
	PlainTextSecrets bool
	IgnoreNonceCount bool
	// Headers used by authenticator. Set to ProxyHeaders to use with
	// proxy server. When nil, NormalHeaders are used.
	Headers *Headers

	/*
	   Approximate size of Client's Cache. When actual number of
	   tracked client nonces exceeds
	   ClientCacheSize+ClientCacheTolerance, ClientCacheTolerance*2
	   older entries are purged.
	*/
	ClientCacheSize      int
	ClientCacheTolerance int

	clients map[string]*digestClient
	mutex   sync.RWMutex
}

// check that DigestAuth implements AuthenticatorInterface
var _ = (AuthenticatorInterface)((*DigestAuth)(nil))

type digestCacheEntry struct {
	nonce    string
	lastSeen int64
}

type digestCache []digestCacheEntry

func (c digestCache) Less(i, j int) bool {
	return c[i].lastSeen < c[j].lastSeen
}

func (c digestCache) Len() int {
	return len(c)
}

func (c digestCache) Swap(i, j int) {
	c[i], c[j] = c[j], c[i]
}

// Purge removes count oldest entries from DigestAuth.clients
func (da *DigestAuth) Purge(count int) {
	da.mutex.Lock()
	defer da.mutex.Unlock()
	entries := make([]digestCacheEntry, 0, len(da.clients))
	for nonce, client := range da.clients {
		entries = append(entries, digestCacheEntry{nonce, client.lastSeen})
	}
	cache := digestCache(entries)
	sort.Sort(cache)
	for _, client := range cache[:count] {
		delete(da.clients, client.nonce)
	}
}

// RequireAuth is an http.HandlerFunc which initiates the
// authentication process (or requires reauthentication).
func (da *DigestAuth) RequireAuth(w http.ResponseWriter, r *http.Request) {
	da.mutex.RLock()
	clientsLen := len(da.clients)
	da.mutex.RUnlock()

	if clientsLen > da.ClientCacheSize+da.ClientCacheTolerance {
		da.Purge(da.ClientCacheTolerance * 2)
	}
	nonce := RandomKey()

	da.mutex.Lock()
	da.clients[nonce] = &digestClient{nc: 0, lastSeen: time.Now().UnixNano()}
	da.mutex.Unlock()

	da.mutex.RLock()
	w.Header().Set(contentType, da.Headers.V().UnauthContentType)
	w.Header().Set(da.Headers.V().Authenticate,
		fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm=MD5, qop="auth"`,
			da.Realm, nonce, da.Opaque))
	w.WriteHeader(da.Headers.V().UnauthCode)
	w.Write([]byte(da.Headers.V().UnauthResponse))
	da.mutex.RUnlock()
}

// DigestAuthParams parses Authorization header from the
// http.Request. Returns a map of auth parameters or nil if the header
// is not a valid parsable Digest auth header.
func DigestAuthParams(authorization string) map[string]string {
	s := strings.SplitN(authorization, " ", 2)
	if len(s) != 2 || s[0] != "Digest" {
		return nil
	}

	return ParsePairs(s[1])
}

// CheckAuth checks whether the request contains valid authentication
// data. Returns a pair of username, authinfo, where username is the
// name of the authenticated user or an empty string and authinfo is
// the contents for the optional Authentication-Info response header.
func (da *DigestAuth) CheckAuth(r *http.Request) (username string, authinfo *string) {
	da.mutex.RLock()
	defer da.mutex.RUnlock()
	username = ""
	authinfo = nil
	auth := DigestAuthParams(r.Header.Get(da.Headers.V().Authorization))
	if auth == nil {
		return "", nil
	}
	// RFC2617 Section 3.2.1 specifies that unset value of algorithm in
	// WWW-Authenticate Response header should be treated as
	// "MD5". According to section 3.2.2 the "algorithm" value in
	// subsequent Request Authorization header must be set to whatever
	// was supplied in the WWW-Authenticate Response header. This
	// implementation always returns an algorithm in WWW-Authenticate
	// header, however there seems to be broken clients in the wild
	// which do not set the algorithm. Assume the unset algorithm in
	// Authorization header to be equal to MD5.
	if _, ok := auth["algorithm"]; !ok {
		auth["algorithm"] = "MD5"
	}
	if da.Opaque != auth["opaque"] || auth["algorithm"] != "MD5" || auth["qop"] != "auth" {
		return "", nil
	}

	// Check if the requested URI matches auth header
	if r.RequestURI != auth["uri"] {
		// We allow auth["uri"] to be a full path prefix of request-uri
		// for some reason lost in history, which is probably wrong, but
		// used to be like that for quite some time
		// (https://tools.ietf.org/html/rfc2617#section-3.2.2 explicitly
		// says that auth["uri"] is the request-uri).
		//
		// TODO: make an option to allow only strict checking.
		switch u, err := url.Parse(auth["uri"]); {
		case err != nil:
			return "", nil
		case r.URL == nil:
			return "", nil
		case len(u.Path) > len(r.URL.Path):
			return "", nil
		case !strings.HasPrefix(r.URL.Path, u.Path):
			return "", nil
		}
	}

	HA1 := da.Secrets(auth["username"], da.Realm)
	if da.PlainTextSecrets {
		HA1 = H(auth["username"] + ":" + da.Realm + ":" + HA1)
	}
	HA2 := H(r.Method + ":" + auth["uri"])
	KD := H(strings.Join([]string{HA1, auth["nonce"], auth["nc"], auth["cnonce"], auth["qop"], HA2}, ":"))

	if subtle.ConstantTimeCompare([]byte(KD), []byte(auth["response"])) != 1 {
		return "", nil
	}

	// At this point crypto checks are completed and validated.
	// Now check if the session is valid.

	nc, err := strconv.ParseUint(auth["nc"], 16, 64)
	if err != nil {
		return "", nil
	}

	client, ok := da.clients[auth["nonce"]]
	if !ok {
		return "", nil
	}
	if client.nc != 0 && client.nc >= nc && !da.IgnoreNonceCount {
		return "", nil
	}
	client.nc = nc
	client.lastSeen = time.Now().UnixNano()

	respHA2 := H(":" + auth["uri"])
	rspauth := H(strings.Join([]string{HA1, auth["nonce"], auth["nc"], auth["cnonce"], auth["qop"], respHA2}, ":"))

	info := fmt.Sprintf(`qop="auth", rspauth="%s", cnonce="%s", nc="%s"`, rspauth, auth["cnonce"], auth["nc"])
	return auth["username"], &info
}

// Default values for ClientCacheSize and ClientCacheTolerance for DigestAuth
const (
	DefaultClientCacheSize      = 1000
	DefaultClientCacheTolerance = 100
)

// Wrap returns an http.HandlerFunc wraps AuthenticatedHandlerFunc
// with this DigestAuth authentication checks. Once the request
// contains valid credentials, it calls wrapped
// AuthenticatedHandlerFunc.
//
// Deprecated: new code should use NewContext instead.
func (da *DigestAuth) Wrap(wrapped AuthenticatedHandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if username, authinfo := da.CheckAuth(r); username == "" {
			da.RequireAuth(w, r)
		} else {
			ar := &AuthenticatedRequest{Request: *r, Username: username}
			if authinfo != nil {
				w.Header().Set(da.Headers.V().AuthInfo, *authinfo)
			}
			wrapped(w, ar)
		}
	}
}

// JustCheck returns a new http.HandlerFunc, which requires
// DigestAuth to successfully authenticate a user before calling
// wrapped http.HandlerFunc.
//
// Authenticated Username is passed as an extra
// X-Authenticated-Username header to the wrapped HandlerFunc.
func (da *DigestAuth) JustCheck(wrapped http.HandlerFunc) http.HandlerFunc {
	return da.Wrap(func(w http.ResponseWriter, ar *AuthenticatedRequest) {
		ar.Header.Set(AuthUsernameHeader, ar.Username)
		wrapped(w, &ar.Request)
	})
}

// NewContext returns a context carrying authentication information for the request.
func (da *DigestAuth) NewContext(ctx context.Context, r *http.Request) context.Context {
	username, authinfo := da.CheckAuth(r)
	da.mutex.Lock()
	defer da.mutex.Unlock()
	info := &Info{Username: username, ResponseHeaders: make(http.Header)}
	if username != "" {
		info.Authenticated = true
		info.ResponseHeaders.Set(da.Headers.V().AuthInfo, *authinfo)
	} else {
		// return back digest WWW-Authenticate header
		if len(da.clients) > da.ClientCacheSize+da.ClientCacheTolerance {
			da.Purge(da.ClientCacheTolerance * 2)
		}
		nonce := RandomKey()
		da.clients[nonce] = &digestClient{nc: 0, lastSeen: time.Now().UnixNano()}
		info.ResponseHeaders.Set(da.Headers.V().Authenticate,
			fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm=MD5, qop="auth"`,
				da.Realm, nonce, da.Opaque))
	}
	return context.WithValue(ctx, infoKey, info)
}

// NewDigestAuthenticator generates a new DigestAuth object
func NewDigestAuthenticator(realm string, secrets SecretProvider) *DigestAuth {
	da := &DigestAuth{
		Opaque:               RandomKey(),
		Realm:                realm,
		Secrets:              secrets,
		PlainTextSecrets:     false,
		ClientCacheSize:      DefaultClientCacheSize,
		ClientCacheTolerance: DefaultClientCacheTolerance,
		clients:              map[string]*digestClient{}}
	return da
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	_ "net/http/pprof"
	"os"

	gclient "github.com/cloudfoundry-incubator/garden/client"
	gconn "github.com/cloudfoundry-incubator/garden/client/connection"
	"github.com/concourse/jettison"
	"github.com/pivotal-golang/lager"
)

var gardenAddr = flag.String(
	"gardenAddr",
	"127.0.0.1:7777",
	"garden API host:port",
)

func main() {
	flag.Parse()

	logger := lager.NewLogger("jettison")
	logger.RegisterSink(lager.NewWriterSink(os.Stdout, lager.DEBUG))

	gardenClient := gclient.New(gconn.New("tcp", *gardenAddr))

	drainer := jettison.NewDrainer(
		logger,
		gardenClient,
	)

	err := drainer.Drain()
	if err != nil {
		logger.Fatal("draining-failed", err)
	}

	logger.Info("drained")
}
<commit_msg>add yeller sink to jettison<commit_after>package main

import (
	"flag"
	_ "net/http/pprof"
	"os"

	gclient "github.com/cloudfoundry-incubator/garden/client"
	gconn "github.com/cloudfoundry-incubator/garden/client/connection"
	"github.com/concourse/jettison"
	"github.com/pivotal-golang/lager"
	"github.com/xoebus/zest"
)

var gardenAddr = flag.String(
	"gardenAddr",
	"127.0.0.1:7777",
	"garden API host:port",
)

var yellerAPIKey = flag.String(
	"yellerAPIKey",
	"",
	"API token to output error logs to Yeller",
)
var yellerEnvironment = flag.String(
	"yellerEnvironment",
	"development",
	"environment label for Yeller",
)

func main() {
	flag.Parse()

	logger := lager.NewLogger("jettison")
	logger.RegisterSink(lager.NewWriterSink(os.Stdout, lager.DEBUG))

	if *yellerAPIKey != "" {
		yellerSink := zest.NewYellerSink(*yellerAPIKey, *yellerEnvironment)
		logger.RegisterSink(yellerSink)
	}

	gardenClient := gclient.New(gconn.New("tcp", *gardenAddr))

	drainer := jettison.NewDrainer(
		logger,
		gardenClient,
	)

	err := drainer.Drain()
	if err != nil {
		logger.Fatal("draining-failed", err)
	}

	logger.Info("drained")
}
<|endoftext|>


<commit_before>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package registry

import (
	"fmt"
	"reflect"
	"sort"

	"launchpad.net/juju-core/errors"
)

// typedNameVersion is a registry that will allow you to register objects based
// on a name and version pair. The objects must be convertible to the Type
// defined when the registry was created. It will be cast during Register so
// you can be sure all objects returned from Get() are safe to TypeAssert to
// that type.
type typedNameVersion struct {
	requiredType reflect.Type
	versions map[string]Versions
}

// NewTypedNameVersion creates a place to register your objects
func NewTypedNameVersion(requiredType reflect.Type) *typedNameVersion {
	return &typedNameVersion{
		requiredType: requiredType,
		versions: make(map[string]Versions),
	}
}

// Description gives the name and available versions in a registry.
type Description struct {
	Name     string
	Versions []int
}

// Versions maps concrete versions of the objects.
type Versions map[int]interface{}

// Register records the factory that can be used to produce an instance of the
// facade at the supplied version.
// If the object being registered doesn't Implement the required Type, then an
// error is returned.
// An error is also returned if an object is already registered with the given
// name and version.
func (r *typedNameVersion) Register(name string, version int, obj interface{}) error {
	if !reflect.TypeOf(obj).ConvertibleTo(r.requiredType) {
		return fmt.Errorf("object of type %T cannot be converted to type %s.%s", obj, r.requiredType.PkgPath(), r.requiredType.Name())
	}
	obj = reflect.ValueOf(obj).Convert(r.requiredType).Interface()
	if r.versions == nil {
		r.versions = make(map[string]Versions, 1)
	}
	if versions, ok := r.versions[name]; ok {
		if _, ok := versions[version]; ok {
			fullname := fmt.Sprintf("%s(%d)", name, version)
			return fmt.Errorf("object %q already registered", fullname)
		}
		versions[version] = obj
	} else {
		r.versions[name] = Versions{version: obj}
	}
	return nil
}

// descriptionFromVersions aggregates the information in a Versions map into a
// more friendly form for List()
func descriptionFromVersions(name string, versions Versions) Description {
	intVersions := make([]int, 0, len(versions))
	for version := range versions {
		intVersions = append(intVersions, version)
	}
	sort.Ints(intVersions)
	return Description{
		Name:     name,
		Versions: intVersions,
	}
}

// List returns a slice describing each of the registered Facades.
func (r *typedNameVersion) List() []Description {
	names := make([]string, 0, len(r.versions))
	for name := range r.versions {
		names = append(names, name)
	}
	sort.Strings(names)
	descriptions := make([]Description, len(r.versions))
	for i, name := range names {
		versions := r.versions[name]
		descriptions[i] = descriptionFromVersions(name, versions)
	}
	return descriptions
}

// Get returns the object for a single name and version. If the requested
// facade is not found, it returns error.NotFound
func (r *typedNameVersion) Get(name string, version int) (interface{}, error) {
	if versions, ok := r.versions[name]; ok {
		if factory, ok := versions[version]; ok {
			return factory, nil
		}
	}
	return nil, errors.NotFoundf("%s(%d)", name, version)
}
<commit_msg>export the type so that other places can have a reference to it.<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package registry

import (
	"fmt"
	"reflect"
	"sort"

	"launchpad.net/juju-core/errors"
)

// TypedNameVersion is a registry that will allow you to register objects based
// on a name and version pair. The objects must be convertible to the Type
// defined when the registry was created. It will be cast during Register so
// you can be sure all objects returned from Get() are safe to TypeAssert to
// that type.
type TypedNameVersion struct {
	requiredType reflect.Type
	versions map[string]Versions
}

// NewTypedNameVersion creates a place to register your objects
func NewTypedNameVersion(requiredType reflect.Type) *TypedNameVersion {
	return &TypedNameVersion{
		requiredType: requiredType,
		versions: make(map[string]Versions),
	}
}

// Description gives the name and available versions in a registry.
type Description struct {
	Name     string
	Versions []int
}

// Versions maps concrete versions of the objects.
type Versions map[int]interface{}

// Register records the factory that can be used to produce an instance of the
// facade at the supplied version.
// If the object being registered doesn't Implement the required Type, then an
// error is returned.
// An error is also returned if an object is already registered with the given
// name and version.
func (r *TypedNameVersion) Register(name string, version int, obj interface{}) error {
	if !reflect.TypeOf(obj).ConvertibleTo(r.requiredType) {
		return fmt.Errorf("object of type %T cannot be converted to type %s.%s", obj, r.requiredType.PkgPath(), r.requiredType.Name())
	}
	obj = reflect.ValueOf(obj).Convert(r.requiredType).Interface()
	if r.versions == nil {
		r.versions = make(map[string]Versions, 1)
	}
	if versions, ok := r.versions[name]; ok {
		if _, ok := versions[version]; ok {
			fullname := fmt.Sprintf("%s(%d)", name, version)
			return fmt.Errorf("object %q already registered", fullname)
		}
		versions[version] = obj
	} else {
		r.versions[name] = Versions{version: obj}
	}
	return nil
}

// descriptionFromVersions aggregates the information in a Versions map into a
// more friendly form for List()
func descriptionFromVersions(name string, versions Versions) Description {
	intVersions := make([]int, 0, len(versions))
	for version := range versions {
		intVersions = append(intVersions, version)
	}
	sort.Ints(intVersions)
	return Description{
		Name:     name,
		Versions: intVersions,
	}
}

// List returns a slice describing each of the registered Facades.
func (r *TypedNameVersion) List() []Description {
	names := make([]string, 0, len(r.versions))
	for name := range r.versions {
		names = append(names, name)
	}
	sort.Strings(names)
	descriptions := make([]Description, len(r.versions))
	for i, name := range names {
		versions := r.versions[name]
		descriptions[i] = descriptionFromVersions(name, versions)
	}
	return descriptions
}

// Get returns the object for a single name and version. If the requested
// facade is not found, it returns error.NotFound
func (r *TypedNameVersion) Get(name string, version int) (interface{}, error) {
	if versions, ok := r.versions[name]; ok {
		if factory, ok := versions[version]; ok {
			return factory, nil
		}
	}
	return nil, errors.NotFoundf("%s(%d)", name, version)
}
<|endoftext|>


<commit_before>package auth

import (
	"crypto/subtle"
	"fmt"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"golang.org/x/net/context"
)

type digestClient struct {
	nc       uint64
	lastSeen int64
}

type DigestAuth struct {
	Realm            string
	Opaque           string
	Secrets          SecretProvider
	PlainTextSecrets bool
	IgnoreNonceCount bool
	// Headers used by authenticator. Set to ProxyHeaders to use with
	// proxy server. When nil, NormalHeaders are used.
	Headers *Headers

	/*
	   Approximate size of Client's Cache. When actual number of
	   tracked client nonces exceeds
	   ClientCacheSize+ClientCacheTolerance, ClientCacheTolerance*2
	   older entries are purged.
	*/
	ClientCacheSize      int
	ClientCacheTolerance int

	clients map[string]*digestClient
	mutex   sync.RWMutex
}

// check that DigestAuth implements AuthenticatorInterface
var _ = (AuthenticatorInterface)((*DigestAuth)(nil))

type digestCacheEntry struct {
	nonce    string
	lastSeen int64
}

type digestCache []digestCacheEntry

func (c digestCache) Less(i, j int) bool {
	return c[i].lastSeen < c[j].lastSeen
}

func (c digestCache) Len() int {
	return len(c)
}

func (c digestCache) Swap(i, j int) {
	c[i], c[j] = c[j], c[i]
}

/*
 Purge removes count oldest entries from DigestAuth.clients
*/
func (da *DigestAuth) Purge(count int) {
	da.mutex.Lock()
	defer da.mutex.Unlock()
	entries := make([]digestCacheEntry, 0, len(da.clients))
	for nonce, client := range da.clients {
		entries = append(entries, digestCacheEntry{nonce, client.lastSeen})
	}
	cache := digestCache(entries)
	sort.Sort(cache)
	for _, client := range cache[:count] {
		delete(da.clients, client.nonce)
	}
}

/*
 http.Handler for DigestAuth which initiates the authentication process
 (or requires reauthentication).
*/
func (da *DigestAuth) RequireAuth(w http.ResponseWriter, r *http.Request) {
	da.mutex.RLock()
	clientsLen := len(da.clients)
	da.mutex.RUnlock()

	if clientsLen > da.ClientCacheSize+da.ClientCacheTolerance {
		da.Purge(da.ClientCacheTolerance * 2)
	}
	nonce := RandomKey()

	da.mutex.Lock()
	da.clients[nonce] = &digestClient{nc: 0, lastSeen: time.Now().UnixNano()}
	da.mutex.Unlock()

	da.mutex.RLock()
	w.Header().Set(contentType, da.Headers.V().UnauthContentType)
	w.Header().Set(da.Headers.V().Authenticate,
		fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm=MD5, qop="auth"`,
			da.Realm, nonce, da.Opaque))
	w.WriteHeader(da.Headers.V().UnauthCode)
	w.Write([]byte(da.Headers.V().UnauthResponse))
	da.mutex.RUnlock()
}

/*
 Parse Authorization header from the http.Request. Returns a map of
 auth parameters or nil if the header is not a valid parsable Digest
 auth header.
*/
func DigestAuthParams(authorization string) map[string]string {
	s := strings.SplitN(authorization, " ", 2)
	if len(s) != 2 || s[0] != "Digest" {
		return nil
	}

	return ParsePairs(s[1])
}

/*
 Check if request contains valid authentication data. Returns a pair
 of username, authinfo where username is the name of the authenticated
 user or an empty string and authinfo is the contents for the optional
 Authentication-Info response header.
*/
func (da *DigestAuth) CheckAuth(r *http.Request) (username string, authinfo *string) {
	da.mutex.RLock()
	defer da.mutex.RUnlock()
	username = ""
	authinfo = nil
	auth := DigestAuthParams(r.Header.Get(da.Headers.V().Authorization))
	if auth == nil {
		return "", nil
	}
	// RFC2617 Section 3.2.1 specifies that unset value of algorithm in
	// WWW-Authenticate Response header should be treated as
	// "MD5". According to section 3.2.2 the "algorithm" value in
	// subsequent Request Authorization header must be set to whatever
	// was supplied in the WWW-Authenticate Response header. This
	// implementation always returns an algorithm in WWW-Authenticate
	// header, however there seems to be broken clients in the wild
	// which do not set the algorithm. Assume the unset algorithm in
	// Authorization header to be equal to MD5.
	if _, ok := auth["algorithm"]; !ok {
		auth["algorithm"] = "MD5"
	}
	if da.Opaque != auth["opaque"] || auth["algorithm"] != "MD5" || auth["qop"] != "auth" {
		return "", nil
	}

	// Check if the requested URI matches auth header
	if r.RequestURI != auth["uri"] {
		// We allow auth["uri"] to be a full path prefix of request-uri
		// for some reason lost in history, which is probably wrong, but
		// used to be like that for quite some time
		// (https://tools.ietf.org/html/rfc2617#section-3.2.2 explicitly
		// says that auth["uri"] is the request-uri).
		//
		// TODO: make an option to allow only strict checking.
		switch u, err := url.Parse(auth["uri"]); {
		case err != nil:
			return "", nil
		case r.URL == nil:
			return "", nil
		case len(u.Path) > len(r.URL.Path):
			return "", nil
		case !strings.HasPrefix(r.URL.Path, u.Path):
			return "", nil
		}
	}

	HA1 := da.Secrets(auth["username"], da.Realm)
	if da.PlainTextSecrets {
		HA1 = H(auth["username"] + ":" + da.Realm + ":" + HA1)
	}
	HA2 := H(r.Method + ":" + auth["uri"])
	KD := H(strings.Join([]string{HA1, auth["nonce"], auth["nc"], auth["cnonce"], auth["qop"], HA2}, ":"))

	if subtle.ConstantTimeCompare([]byte(KD), []byte(auth["response"])) != 1 {
		return "", nil
	}

	// At this point crypto checks are completed and validated.
	// Now check if the session is valid.

	nc, err := strconv.ParseUint(auth["nc"], 16, 64)
	if err != nil {
		return "", nil
	}

	if client, ok := da.clients[auth["nonce"]]; !ok {
		return "", nil
	} else {
		if client.nc != 0 && client.nc >= nc && !da.IgnoreNonceCount {
			return "", nil
		}
		client.nc = nc
		client.lastSeen = time.Now().UnixNano()
	}

	respHA2 := H(":" + auth["uri"])
	rspauth := H(strings.Join([]string{HA1, auth["nonce"], auth["nc"], auth["cnonce"], auth["qop"], respHA2}, ":"))

	info := fmt.Sprintf(`qop="auth", rspauth="%s", cnonce="%s", nc="%s"`, rspauth, auth["cnonce"], auth["nc"])
	return auth["username"], &info
}

/*
 Default values for ClientCacheSize and ClientCacheTolerance for DigestAuth
*/
const DefaultClientCacheSize = 1000
const DefaultClientCacheTolerance = 100

/*
 Wrap returns an Authenticator which uses HTTP Digest
 authentication. Arguments:

 realm: The authentication realm.

 secrets: SecretProvider which must return HA1 digests for the same
 realm as above.
*/
func (da *DigestAuth) Wrap(wrapped AuthenticatedHandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if username, authinfo := da.CheckAuth(r); username == "" {
			da.RequireAuth(w, r)
		} else {
			ar := &AuthenticatedRequest{Request: *r, Username: username}
			if authinfo != nil {
				w.Header().Set(da.Headers.V().AuthInfo, *authinfo)
			}
			wrapped(w, ar)
		}
	}
}

/*
 JustCheck returns function which converts an http.HandlerFunc into a
 http.HandlerFunc which requires authentication. Username is passed as
 an extra X-Authenticated-Username header.
*/
func (da *DigestAuth) JustCheck(wrapped http.HandlerFunc) http.HandlerFunc {
	return da.Wrap(func(w http.ResponseWriter, ar *AuthenticatedRequest) {
		ar.Header.Set(AuthUsernameHeader, ar.Username)
		wrapped(w, &ar.Request)
	})
}

// NewContext returns a context carrying authentication information for the request.
func (da *DigestAuth) NewContext(ctx context.Context, r *http.Request) context.Context {
	da.mutex.Lock()
	defer da.mutex.Unlock()
	username, authinfo := da.CheckAuth(r)
	info := &Info{Username: username, ResponseHeaders: make(http.Header)}
	if username != "" {
		info.Authenticated = true
		info.ResponseHeaders.Set(da.Headers.V().AuthInfo, *authinfo)
	} else {
		// return back digest WWW-Authenticate header
		if len(da.clients) > da.ClientCacheSize+da.ClientCacheTolerance {
			da.Purge(da.ClientCacheTolerance * 2)
		}
		nonce := RandomKey()
		da.clients[nonce] = &digestClient{nc: 0, lastSeen: time.Now().UnixNano()}
		info.ResponseHeaders.Set(da.Headers.V().Authenticate,
			fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm=MD5, qop="auth"`,
				da.Realm, nonce, da.Opaque))
	}
	return context.WithValue(ctx, infoKey, info)
}

// NewDigestAuthenticator generates a new DigestAuth object
func NewDigestAuthenticator(realm string, secrets SecretProvider) *DigestAuth {
	da := &DigestAuth{
		Opaque:               RandomKey(),
		Realm:                realm,
		Secrets:              secrets,
		PlainTextSecrets:     false,
		ClientCacheSize:      DefaultClientCacheSize,
		ClientCacheTolerance: DefaultClientCacheTolerance,
		clients:              map[string]*digestClient{}}
	return da
}
<commit_msg>Drop else after if ending with return statement.<commit_after>package auth

import (
	"crypto/subtle"
	"fmt"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"golang.org/x/net/context"
)

type digestClient struct {
	nc       uint64
	lastSeen int64
}

type DigestAuth struct {
	Realm            string
	Opaque           string
	Secrets          SecretProvider
	PlainTextSecrets bool
	IgnoreNonceCount bool
	// Headers used by authenticator. Set to ProxyHeaders to use with
	// proxy server. When nil, NormalHeaders are used.
	Headers *Headers

	/*
	   Approximate size of Client's Cache. When actual number of
	   tracked client nonces exceeds
	   ClientCacheSize+ClientCacheTolerance, ClientCacheTolerance*2
	   older entries are purged.
	*/
	ClientCacheSize      int
	ClientCacheTolerance int

	clients map[string]*digestClient
	mutex   sync.RWMutex
}

// check that DigestAuth implements AuthenticatorInterface
var _ = (AuthenticatorInterface)((*DigestAuth)(nil))

type digestCacheEntry struct {
	nonce    string
	lastSeen int64
}

type digestCache []digestCacheEntry

func (c digestCache) Less(i, j int) bool {
	return c[i].lastSeen < c[j].lastSeen
}

func (c digestCache) Len() int {
	return len(c)
}

func (c digestCache) Swap(i, j int) {
	c[i], c[j] = c[j], c[i]
}

/*
 Purge removes count oldest entries from DigestAuth.clients
*/
func (da *DigestAuth) Purge(count int) {
	da.mutex.Lock()
	defer da.mutex.Unlock()
	entries := make([]digestCacheEntry, 0, len(da.clients))
	for nonce, client := range da.clients {
		entries = append(entries, digestCacheEntry{nonce, client.lastSeen})
	}
	cache := digestCache(entries)
	sort.Sort(cache)
	for _, client := range cache[:count] {
		delete(da.clients, client.nonce)
	}
}

/*
 http.Handler for DigestAuth which initiates the authentication process
 (or requires reauthentication).
*/
func (da *DigestAuth) RequireAuth(w http.ResponseWriter, r *http.Request) {
	da.mutex.RLock()
	clientsLen := len(da.clients)
	da.mutex.RUnlock()

	if clientsLen > da.ClientCacheSize+da.ClientCacheTolerance {
		da.Purge(da.ClientCacheTolerance * 2)
	}
	nonce := RandomKey()

	da.mutex.Lock()
	da.clients[nonce] = &digestClient{nc: 0, lastSeen: time.Now().UnixNano()}
	da.mutex.Unlock()

	da.mutex.RLock()
	w.Header().Set(contentType, da.Headers.V().UnauthContentType)
	w.Header().Set(da.Headers.V().Authenticate,
		fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm=MD5, qop="auth"`,
			da.Realm, nonce, da.Opaque))
	w.WriteHeader(da.Headers.V().UnauthCode)
	w.Write([]byte(da.Headers.V().UnauthResponse))
	da.mutex.RUnlock()
}

/*
 Parse Authorization header from the http.Request. Returns a map of
 auth parameters or nil if the header is not a valid parsable Digest
 auth header.
*/
func DigestAuthParams(authorization string) map[string]string {
	s := strings.SplitN(authorization, " ", 2)
	if len(s) != 2 || s[0] != "Digest" {
		return nil
	}

	return ParsePairs(s[1])
}

/*
 Check if request contains valid authentication data. Returns a pair
 of username, authinfo where username is the name of the authenticated
 user or an empty string and authinfo is the contents for the optional
 Authentication-Info response header.
*/
func (da *DigestAuth) CheckAuth(r *http.Request) (username string, authinfo *string) {
	da.mutex.RLock()
	defer da.mutex.RUnlock()
	username = ""
	authinfo = nil
	auth := DigestAuthParams(r.Header.Get(da.Headers.V().Authorization))
	if auth == nil {
		return "", nil
	}
	// RFC2617 Section 3.2.1 specifies that unset value of algorithm in
	// WWW-Authenticate Response header should be treated as
	// "MD5". According to section 3.2.2 the "algorithm" value in
	// subsequent Request Authorization header must be set to whatever
	// was supplied in the WWW-Authenticate Response header. This
	// implementation always returns an algorithm in WWW-Authenticate
	// header, however there seems to be broken clients in the wild
	// which do not set the algorithm. Assume the unset algorithm in
	// Authorization header to be equal to MD5.
	if _, ok := auth["algorithm"]; !ok {
		auth["algorithm"] = "MD5"
	}
	if da.Opaque != auth["opaque"] || auth["algorithm"] != "MD5" || auth["qop"] != "auth" {
		return "", nil
	}

	// Check if the requested URI matches auth header
	if r.RequestURI != auth["uri"] {
		// We allow auth["uri"] to be a full path prefix of request-uri
		// for some reason lost in history, which is probably wrong, but
		// used to be like that for quite some time
		// (https://tools.ietf.org/html/rfc2617#section-3.2.2 explicitly
		// says that auth["uri"] is the request-uri).
		//
		// TODO: make an option to allow only strict checking.
		switch u, err := url.Parse(auth["uri"]); {
		case err != nil:
			return "", nil
		case r.URL == nil:
			return "", nil
		case len(u.Path) > len(r.URL.Path):
			return "", nil
		case !strings.HasPrefix(r.URL.Path, u.Path):
			return "", nil
		}
	}

	HA1 := da.Secrets(auth["username"], da.Realm)
	if da.PlainTextSecrets {
		HA1 = H(auth["username"] + ":" + da.Realm + ":" + HA1)
	}
	HA2 := H(r.Method + ":" + auth["uri"])
	KD := H(strings.Join([]string{HA1, auth["nonce"], auth["nc"], auth["cnonce"], auth["qop"], HA2}, ":"))

	if subtle.ConstantTimeCompare([]byte(KD), []byte(auth["response"])) != 1 {
		return "", nil
	}

	// At this point crypto checks are completed and validated.
	// Now check if the session is valid.

	nc, err := strconv.ParseUint(auth["nc"], 16, 64)
	if err != nil {
		return "", nil
	}

	client, ok := da.clients[auth["nonce"]]
	if !ok {
		return "", nil
	}
	if client.nc != 0 && client.nc >= nc && !da.IgnoreNonceCount {
		return "", nil
	}
	client.nc = nc
	client.lastSeen = time.Now().UnixNano()

	respHA2 := H(":" + auth["uri"])
	rspauth := H(strings.Join([]string{HA1, auth["nonce"], auth["nc"], auth["cnonce"], auth["qop"], respHA2}, ":"))

	info := fmt.Sprintf(`qop="auth", rspauth="%s", cnonce="%s", nc="%s"`, rspauth, auth["cnonce"], auth["nc"])
	return auth["username"], &info
}

/*
 Default values for ClientCacheSize and ClientCacheTolerance for DigestAuth
*/
const DefaultClientCacheSize = 1000
const DefaultClientCacheTolerance = 100

/*
 Wrap returns an Authenticator which uses HTTP Digest
 authentication. Arguments:

 realm: The authentication realm.

 secrets: SecretProvider which must return HA1 digests for the same
 realm as above.
*/
func (da *DigestAuth) Wrap(wrapped AuthenticatedHandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if username, authinfo := da.CheckAuth(r); username == "" {
			da.RequireAuth(w, r)
		} else {
			ar := &AuthenticatedRequest{Request: *r, Username: username}
			if authinfo != nil {
				w.Header().Set(da.Headers.V().AuthInfo, *authinfo)
			}
			wrapped(w, ar)
		}
	}
}

/*
 JustCheck returns function which converts an http.HandlerFunc into a
 http.HandlerFunc which requires authentication. Username is passed as
 an extra X-Authenticated-Username header.
*/
func (da *DigestAuth) JustCheck(wrapped http.HandlerFunc) http.HandlerFunc {
	return da.Wrap(func(w http.ResponseWriter, ar *AuthenticatedRequest) {
		ar.Header.Set(AuthUsernameHeader, ar.Username)
		wrapped(w, &ar.Request)
	})
}

// NewContext returns a context carrying authentication information for the request.
func (da *DigestAuth) NewContext(ctx context.Context, r *http.Request) context.Context {
	da.mutex.Lock()
	defer da.mutex.Unlock()
	username, authinfo := da.CheckAuth(r)
	info := &Info{Username: username, ResponseHeaders: make(http.Header)}
	if username != "" {
		info.Authenticated = true
		info.ResponseHeaders.Set(da.Headers.V().AuthInfo, *authinfo)
	} else {
		// return back digest WWW-Authenticate header
		if len(da.clients) > da.ClientCacheSize+da.ClientCacheTolerance {
			da.Purge(da.ClientCacheTolerance * 2)
		}
		nonce := RandomKey()
		da.clients[nonce] = &digestClient{nc: 0, lastSeen: time.Now().UnixNano()}
		info.ResponseHeaders.Set(da.Headers.V().Authenticate,
			fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm=MD5, qop="auth"`,
				da.Realm, nonce, da.Opaque))
	}
	return context.WithValue(ctx, infoKey, info)
}

// NewDigestAuthenticator generates a new DigestAuth object
func NewDigestAuthenticator(realm string, secrets SecretProvider) *DigestAuth {
	da := &DigestAuth{
		Opaque:               RandomKey(),
		Realm:                realm,
		Secrets:              secrets,
		PlainTextSecrets:     false,
		ClientCacheSize:      DefaultClientCacheSize,
		ClientCacheTolerance: DefaultClientCacheTolerance,
		clients:              map[string]*digestClient{}}
	return da
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"os"

	"junta/paxos"
	"junta/store"
	"junta/util"
	"junta/server"
)

const (
	alpha = 50
	idBits = 160
)

// Flags
var (
	listenAddr *string = flag.String("l", ":8040", "The address to bind to.")
	attachAddr *string = flag.String("a", "", "The address to bind to.")
)

func main() {
	flag.Parse()

	util.LogWriter = os.Stderr

	outs := make(chan paxos.Msg)

	self := util.RandHexString(idBits)
	st := store.New()

	seqn := uint64(0)
	seqn = addMember(st, seqn + 1, self, *listenAddr)
	seqn = claimSlot(st, seqn + 1, "1", self)
	seqn = claimLeader(st, seqn + 1, self)

	mg := paxos.NewManager(self, seqn, alpha, st, paxos.ChanPutCloser(outs))

	// Skip ahead alpha steps so that the registrar can provide a meaningful
	// cluster.
	for i := seqn + 1; i < seqn + alpha; i++ {
		go st.Apply(i, "") // nop
	}

	sv := &server.Server{*listenAddr, st, mg}

	go func() {
		panic(sv.ListenAndServe())
	}()

	go func() {
		panic(sv.ListenAndServeUdp(outs))
	}()

	for {
		st.Apply(mg.Recv())
	}
}

func addMember(st *store.Store, seqn uint64, self, addr string) uint64 {
	// TODO pull out path as a const
	mx, err := store.EncodeSet("/j/junta/members/"+self, addr, store.Missing)
	if err != nil {
		panic(err)
	}
	st.Apply(seqn, mx)
	return seqn
}

func claimSlot(st *store.Store, seqn uint64, slot, self string) uint64 {
	// TODO pull out path as a const
	mx, err := store.EncodeSet("/j/junta/slot/"+slot, self, store.Missing)
	if err != nil {
		panic(err)
	}
	st.Apply(seqn, mx)
	return seqn
}

func claimLeader(st *store.Store, seqn uint64, self string) uint64 {
	// TODO pull out path as a const
	mx, err := store.EncodeSet("/j/junta/leader", self, store.Missing)
	if err != nil {
		panic(err)
	}
	st.Apply(seqn, mx)
	return seqn
}
<commit_msg>default listen addr<commit_after>package main

import (
	"flag"
	"os"

	"junta/paxos"
	"junta/store"
	"junta/util"
	"junta/server"
)

const (
	alpha = 50
	idBits = 160
)

// Flags
var (
	listenAddr *string = flag.String("l", "[::1]:8040", "The address to bind to.")
	attachAddr *string = flag.String("a", "", "The address to bind to.")
)

func main() {
	flag.Parse()

	util.LogWriter = os.Stderr

	outs := make(chan paxos.Msg)

	self := util.RandHexString(idBits)
	st := store.New()

	seqn := uint64(0)
	seqn = addMember(st, seqn + 1, self, *listenAddr)
	seqn = claimSlot(st, seqn + 1, "1", self)
	seqn = claimLeader(st, seqn + 1, self)

	mg := paxos.NewManager(self, seqn, alpha, st, paxos.ChanPutCloser(outs))

	// Skip ahead alpha steps so that the registrar can provide a meaningful
	// cluster.
	for i := seqn + 1; i < seqn + alpha; i++ {
		go st.Apply(i, "") // nop
	}

	sv := &server.Server{*listenAddr, st, mg}

	go func() {
		panic(sv.ListenAndServe())
	}()

	go func() {
		panic(sv.ListenAndServeUdp(outs))
	}()

	for {
		st.Apply(mg.Recv())
	}
}

func addMember(st *store.Store, seqn uint64, self, addr string) uint64 {
	// TODO pull out path as a const
	mx, err := store.EncodeSet("/j/junta/members/"+self, addr, store.Missing)
	if err != nil {
		panic(err)
	}
	st.Apply(seqn, mx)
	return seqn
}

func claimSlot(st *store.Store, seqn uint64, slot, self string) uint64 {
	// TODO pull out path as a const
	mx, err := store.EncodeSet("/j/junta/slot/"+slot, self, store.Missing)
	if err != nil {
		panic(err)
	}
	st.Apply(seqn, mx)
	return seqn
}

func claimLeader(st *store.Store, seqn uint64, self string) uint64 {
	// TODO pull out path as a const
	mx, err := store.EncodeSet("/j/junta/leader", self, store.Missing)
	if err != nil {
		panic(err)
	}
	st.Apply(seqn, mx)
	return seqn
}
<|endoftext|>


<commit_before>// Copyright 2017 aerth <aerth@riseup.net>

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"math"
	"os"
	"time"

	"golang.org/x/image/colornames"

	"github.com/aerth/rpg"
	"github.com/faiface/pixel"
	"github.com/faiface/pixel/pixelgl"
)

var LEVEL string

func FlagInit() {
	log.SetFlags(log.Lshortfile)
	log.SetPrefix("> ")
	if flag.NArg() != 1 {
		fmt.Println("Which map name?")
		os.Exit(111)
	}
	LEVEL = flag.Arg(0)

}

var convert = flag.Bool("danger", false, "convert old to new (experimental)")
var (
	IM = pixel.IM
	ZV = pixel.ZV
)

var helpText = "ENTER=save LEFT=block RIGHT=tile SHIFT=batch SPACE=del CAPS=highlight U=undo R=redo 4=turbo B=dontreplace"

func loadSpriteSheet() (pixel.Picture, []*pixel.Sprite) {
	spritesheet, err := rpg.LoadPicture("sprites/tileset.png")
	/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
	 * 1
	 * 2
	 * ...
	 * 16
	 */
	if err != nil {

		panic(err)

	}
	var sheetFrames []pixel.Rect
	for x := spritesheet.Bounds().Min.X; x < spritesheet.Bounds().Max.X; x += 32 {
		for y := spritesheet.Bounds().Min.Y; y < spritesheet.Bounds().Max.Y; y += 32 {
			sheetFrames = append(sheetFrames, pixel.R(x, y, x+32, y+32))
		}
	}

	var spritemap = []*pixel.Sprite{}
	for i := 0; i < len(sheetFrames); i++ {
		x := i
		spritemap = append(spritemap, pixel.NewSprite(spritesheet, sheetFrames[x]))
	}
	log.Println(len(spritemap), "sprites loaded")
	log.Println(spritemap[0].Frame())
	return spritesheet, spritemap
}

func run() {
	flag.Parse()
	FlagInit()
	cfg := pixelgl.WindowConfig{
		Title:     "AERPG mapedit",
		Bounds:    pixel.R(0, 0, 800, 600),
		Resizable: true,
		VSync:     false,
	}
	win, err := pixelgl.NewWindow(cfg)
	if err != nil {
		panic(err)
	}
	var oldthings = []rpg.Object{}
	if b, err := ioutil.ReadFile(LEVEL); err == nil {
		err = json.Unmarshal(b, &oldthings)
		if err != nil {
			panic(err)
		}
	}
	var things []rpg.Object
	for _, v := range oldthings {
		if *convert {
			log.Println("Converting")
			v.Type = rpg.O_TILE
			if v.SpriteNum == 53 && v.Type == rpg.O_TILE {
				v.Type = rpg.O_BLOCK
			}
		}

		v.Rect = rpg.DefaultSpriteRectangle.Moved(v.Loc)

		things = append(things, v)

	}

	spritesheet, spritemap := loadSpriteSheet()

	batch := pixel.NewBatch(&pixel.TrianglesData{}, spritesheet)
	start := time.Now()
	second := time.Tick(time.Second)
	last := start
	frames := 0

	var (
		camPos       = pixel.ZV
		camSpeed     = 500.0
		camZoom      = 1.0
		camZoomSpeed = 1.2
	)
	currentThing := 20 // 20 is grass,  0 should be transparent sprite
	text := rpg.NewTextSmooth(14)
	fmt.Fprint(text, helpText)
	cursor := rpg.GetCursor(0)
	undobuffer := []rpg.Object{}
	var turbo = false
	var highlight = true
	var box pixel.Rect
	var replace = true
	for !win.Closed() {
		dt := time.Since(last).Seconds()
		_ = dt
		last = time.Now()
		frames++

		// camera
		cam := pixel.IM.Scaled(camPos, camZoom).Moved(win.Bounds().Center().Sub(camPos))
		camZoom *= math.Pow(camZoomSpeed, win.MouseScroll().Y)
		win.SetMatrix(cam)

		// snap to grid
		snap := 32.00 // 16 for half grid ?
		mouse := cam.Unproject(win.MousePosition())
		mouse.X = float64(int(mouse.X/snap)) * snap
		mouse.Y = float64(int(mouse.Y/snap)) * snap
		mouse.X = mouse.X - 16
		mouse.Y = mouse.Y - 16
		if win.JustPressed(pixelgl.Key4) {
			turbo = !turbo
			log.Println("turbo:", turbo)
		}
		if win.JustPressed(pixelgl.KeyCapsLock) {
			highlight = !highlight
			log.Println("highlight:", highlight)
		}

		if turbo {
			dt *= 8
		}

		if win.JustPressed(pixelgl.KeyU) {
			undobuffer = append(undobuffer, things[len(things)-1])
			things = things[:len(things)-1]
		}
		if win.JustPressed(pixelgl.KeyR) {
			if len(undobuffer) > 0 {
				things = append(things, undobuffer[len(undobuffer)-1])
				if !win.Pressed(pixelgl.KeyLeftShift) {
					undobuffer = undobuffer[:len(undobuffer)-1]
				}
			} else {
				log.Println("no undo buffer")
			}
		}

		deleteThing := func(loc pixel.Vec) []rpg.Object {
			var newthings []rpg.Object
			for _, thing := range things {
				if thing.Rect.Contains(mouse) {
					log.Println("deleting:", thing)
				} else {

					newthings = append(newthings, thing)
				}
			}
			return newthings
		}
		if win.Pressed(pixelgl.KeySpace) {
			things = deleteThing(mouse)
		}
		if win.JustPressed(pixelgl.KeyB) {
			replace = !replace
			log.Println("replace:", replace)
		}
		// draw big patch of grass
		if win.Pressed(pixelgl.KeyLeftControl) && (win.JustPressed(pixelgl.MouseButtonLeft) || win.JustPressed(pixelgl.MouseButtonRight)) {
			box.Min.Y = mouse.Y
			box.Min.X = mouse.X
		} else {
			if win.Pressed(pixelgl.KeyLeftShift) && win.Pressed(pixelgl.MouseButtonRight) ||
				win.JustPressed(pixelgl.MouseButtonRight) {
				thing := rpg.NewBlock(mouse)
				thing.SpriteNum = currentThing
				log.Println("Stamping Block", mouse, thing.SpriteNum)
				if replace {
					undobuffer = append(undobuffer, thing)
					things = append(deleteThing(mouse), thing)
				} else {
					things = append(things, thing)

				}
			}
			if win.Pressed(pixelgl.KeyLeftShift) && win.Pressed(pixelgl.MouseButtonLeft) ||
				win.JustPressed(pixelgl.MouseButtonLeft) {
				thing := rpg.NewTile(mouse)
				thing.SpriteNum = currentThing
				log.Println("Stamping Tile", mouse, thing.SpriteNum)
				if replace {
					undobuffer = append(undobuffer, thing)
					things = append(deleteThing(mouse), thing)
				} else {
					things = append(things, thing)
				}
			}
		}
		if win.JustPressed(pixelgl.KeyEnter) {
			b, err := json.Marshal(things)
			if err != nil {
				panic(err)
			}
			os.Rename(LEVEL, LEVEL+".old")
			if err := ioutil.WriteFile(LEVEL, b, 0644); err != nil {
				log.Println(LEVEL + " map saved")
			}
		}
		if win.JustPressed(pixelgl.KeyPageUp) {
			currentThing++
			if currentThing > len(spritemap)-1 {
				currentThing = 0
			}
			log.Println("current sprite:", currentThing)
		}
		if win.JustPressed(pixelgl.KeyPageDown) {
			currentThing--
			if currentThing <= 0 {
				currentThing = len(spritemap) - 1
			}
			log.Println("current sprite:", currentThing)
		}
		if win.Pressed(pixelgl.KeyLeft) || win.Pressed(pixelgl.KeyA) {
			camPos.X -= camSpeed * dt
		}
		if win.Pressed(pixelgl.KeyRight) || win.Pressed(pixelgl.KeyD) {
			camPos.X += camSpeed * dt
		}
		if win.Pressed(pixelgl.KeyDown) || win.Pressed(pixelgl.KeyS) {
			camPos.Y -= camSpeed * dt
		}
		if win.Pressed(pixelgl.KeyUp) || win.Pressed(pixelgl.KeyW) {
			camPos.Y += camSpeed * dt
		}

		//	canvas.Clear(pixel.Alpha(0))
		win.Clear(colornames.Green)
		batch.Clear()

		batch.Draw(win)
		if b := box.Size(); b.Len() != 0 {
			if win.Pressed(pixelgl.KeyLeftControl) {
				if win.JustReleased(pixelgl.MouseButtonLeft) {
					box.Max = mouse
					box = box.Norm()
					log.Println("drawing rectangle:", box, currentThing)
					things = append(DeleteThings(things, box), rpg.DrawPatternObject(currentThing, rpg.O_TILE, box, 100)...)
				}
				if win.JustReleased(pixelgl.MouseButtonRight) {
					box.Max = mouse
					box = box.Norm()
					log.Println("drawing rectangle:", box, currentThing)
					things = append(DeleteThings(things, box), rpg.DrawPatternObject(currentThing, rpg.O_BLOCK, box, 100)...)

				}
			}
		}

		for i := range things {
			things[i].Draw(batch, spritesheet, spritemap)
			if highlight {
				things[i].Highlight(batch)
			}
			if things[i].Rect.Contains(mouse) {
				things[i].Highlight(batch)

			}

		}

		batch.Draw(win)

		// draw player spawn
		spritemap[182].Draw(win, IM.Scaled(ZV, 2).Moved(pixel.V(16, 16))) // incorrect offset

		// return cam
		win.SetMatrix(IM)
		spritemap[currentThing].Draw(win, IM.Scaled(ZV, 2).Moved(pixel.V(64, 64)).Moved(spritemap[0].Frame().Center()))
		text.Draw(win, IM.Moved(pixel.V(10, 10)))
		cursor.Draw(win, IM.Moved(win.MousePosition()).Moved(pixel.V(32, -32)))

		win.Update()

		select {
		default: //
		case <-second:
			//	log.Println("Offset:", offset)
			log.Println("Last DT", dt)
			log.Println("FPS:", frames)
			log.Printf("things: %v", len(things))
			//log.Printf("dynamic things: %v", len(world.DObjects))
			frames = 0
		}
	}
}

func main() {
	pixelgl.Run(run)
}

func DeleteThings(from []rpg.Object, at pixel.Rect) []rpg.Object {
	var cleaned []rpg.Object
	for _, o := range from {
		if !at.Contains(o.Loc) {
			cleaned = append(cleaned, o)
		}

	}
	return cleaned
}
<commit_msg>will work again<commit_after>// Copyright 2017 aerth <aerth@riseup.net>

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"math"
	"os"
	"time"

	"golang.org/x/image/colornames"

	"github.com/aerth/rpg"
	"github.com/faiface/pixel"
	"github.com/faiface/pixel/pixelgl"
)

var LEVEL string

func FlagInit() {
	log.SetFlags(log.Lshortfile)
	log.SetPrefix("> ")
	if flag.NArg() != 1 {
		fmt.Println("Which map name?")
		os.Exit(111)
	}
	LEVEL = flag.Arg(0)

}

var convert = flag.Bool("danger", false, "convert old to new (experimental)")
var (
	IM = pixel.IM
	ZV = pixel.ZV
)

var helpText = "ENTER=save LEFT=block RIGHT=tile SHIFT=batch SPACE=del CAPS=highlight U=undo R=redo 4=turbo B=dontreplace"

func loadSpriteSheet() (pixel.Picture, []*pixel.Sprite) {
	spritesheet, err := rpg.LoadPicture("sprites/tileset.png")
	/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
	 * 1
	 * 2
	 * ...
	 * 16
	 */
	if err != nil {

		panic(err)

	}
	var sheetFrames []pixel.Rect
	for x := spritesheet.Bounds().Min.X; x < spritesheet.Bounds().Max.X; x += 32 {
		for y := spritesheet.Bounds().Min.Y; y < spritesheet.Bounds().Max.Y; y += 32 {
			sheetFrames = append(sheetFrames, pixel.R(x, y, x+32, y+32))
		}
	}

	var spritemap = []*pixel.Sprite{}
	for i := 0; i < len(sheetFrames); i++ {
		x := i
		spritemap = append(spritemap, pixel.NewSprite(spritesheet, sheetFrames[x]))
	}
	log.Println(len(spritemap), "sprites loaded")
	log.Println(spritemap[0].Frame())
	return spritesheet, spritemap
}

func run() {
	flag.Parse()
	FlagInit()
	cfg := pixelgl.WindowConfig{
		Title:     "AERPG mapedit",
		Bounds:    pixel.R(0, 0, 800, 600),
		Resizable: true,
		VSync:     false,
	}
	win, err := pixelgl.NewWindow(cfg)
	if err != nil {
		panic(err)
	}
	var oldthings = []rpg.Object{}
	if b, err := ioutil.ReadFile(LEVEL); err == nil {
		err = json.Unmarshal(b, &oldthings)
		if err != nil {
			panic(err)
		}
	}
	var things []rpg.Object
	for _, v := range oldthings {
		if *convert {
			log.Println("Converting")
			v.Type = rpg.O_TILE
			if v.SpriteNum == 53 && v.Type == rpg.O_TILE {
				v.Type = rpg.O_BLOCK
			}
		}

		v.Rect = rpg.DefaultSpriteRectangle.Moved(v.Loc)

		things = append(things, v)

	}

	spritesheet, spritemap := loadSpriteSheet()

	batch := pixel.NewBatch(&pixel.TrianglesData{}, spritesheet)
	start := time.Now()
	second := time.Tick(time.Second)
	last := start
	frames := 0

	var (
		camPos       = pixel.ZV
		camSpeed     = 500.0
		camZoom      = 1.0
		camZoomSpeed = 1.2
	)
	currentThing := 20 // 20 is grass,  0 should be transparent sprite
	text := rpg.NewTextSmooth(14)
	fmt.Fprint(text, helpText)
	cursor := rpg.GetCursor(0)
	undobuffer := []rpg.Object{}
	var turbo = false
	var highlight = true
	var box pixel.Rect
	var replace = true
	for !win.Closed() {
		dt := time.Since(last).Seconds()
		_ = dt
		last = time.Now()
		frames++

		// camera
		cam := pixel.IM.Scaled(camPos, camZoom).Moved(win.Bounds().Center().Sub(camPos))
		camZoom *= math.Pow(camZoomSpeed, win.MouseScroll().Y)
		win.SetMatrix(cam)

		// snap to grid
		snap := 32.00 // 16 for half grid ?
		mouse := cam.Unproject(win.MousePosition())
		mouse.X = float64(int(mouse.X/snap)) * snap
		mouse.Y = float64(int(mouse.Y/snap)) * snap
		mouse.X = mouse.X - 16
		mouse.Y = mouse.Y - 16
		if win.JustPressed(pixelgl.Key4) {
			turbo = !turbo
			log.Println("turbo:", turbo)
		}
		if win.JustPressed(pixelgl.KeyCapsLock) {
			highlight = !highlight
			log.Println("highlight:", highlight)
		}

		if turbo {
			dt *= 8
		}

		if win.JustPressed(pixelgl.KeyU) {
			undobuffer = append(undobuffer, things[len(things)-1])
			things = things[:len(things)-1]
		}
		if win.JustPressed(pixelgl.KeyR) {
			if len(undobuffer) > 0 {
				things = append(things, undobuffer[len(undobuffer)-1])
				if !win.Pressed(pixelgl.KeyLeftShift) {
					undobuffer = undobuffer[:len(undobuffer)-1]
				}
			} else {
				log.Println("no undo buffer")
			}
		}

		deleteThing := func(loc pixel.Vec) []rpg.Object {
			var newthings []rpg.Object
			for _, thing := range things {
				if thing.Rect.Contains(mouse) {
					log.Println("deleting:", thing)
				} else {

					newthings = append(newthings, thing)
				}
			}
			return newthings
		}
		if win.Pressed(pixelgl.KeySpace) {
			things = deleteThing(mouse)
		}
		if win.JustPressed(pixelgl.KeyB) {
			replace = !replace
			log.Println("replace:", replace)
		}
		// draw big patch of grass
		if win.Pressed(pixelgl.KeyLeftControl) && (win.JustPressed(pixelgl.MouseButtonLeft) || win.JustPressed(pixelgl.MouseButtonRight)) {
			box.Min.Y = mouse.Y
			box.Min.X = mouse.X
		} else {
			if win.Pressed(pixelgl.KeyLeftShift) && win.Pressed(pixelgl.MouseButtonRight) ||
				win.JustPressed(pixelgl.MouseButtonRight) {
				thing := rpg.NewBlock(mouse)
				thing.SpriteNum = currentThing
				log.Println("Stamping Block", mouse, thing.SpriteNum)
				if replace {
					undobuffer = append(undobuffer, thing)
					things = append(deleteThing(mouse), thing)
				} else {
					things = append(things, thing)

				}
			}
			if win.Pressed(pixelgl.KeyLeftShift) && win.Pressed(pixelgl.MouseButtonLeft) ||
				win.JustPressed(pixelgl.MouseButtonLeft) {
				thing := rpg.NewTile(mouse)
				thing.SpriteNum = currentThing
				log.Println("Stamping Tile", mouse, thing.SpriteNum)
				if replace {
					undobuffer = append(undobuffer, thing)
					things = append(deleteThing(mouse), thing)
				} else {
					things = append(things, thing)
				}
			}
		}
		if win.JustPressed(pixelgl.KeyEnter) {
			b, err := json.Marshal(things)
			if err != nil {
				panic(err)
			}
			os.Rename(LEVEL, LEVEL+".old")
			if err := ioutil.WriteFile(LEVEL, b, 0644); err != nil {
				log.Println(LEVEL + " map saved")
			}
		}
		if win.JustPressed(pixelgl.KeyPageUp) {
			currentThing++
			if currentThing > len(spritemap)-1 {
				currentThing = 0
			}
			log.Println("current sprite:", currentThing)
		}
		if win.JustPressed(pixelgl.KeyPageDown) {
			currentThing--
			if currentThing <= 0 {
				currentThing = len(spritemap) - 1
			}
			log.Println("current sprite:", currentThing)
		}
		if win.Pressed(pixelgl.KeyLeft) || win.Pressed(pixelgl.KeyA) {
			camPos.X -= camSpeed * dt
		}
		if win.Pressed(pixelgl.KeyRight) || win.Pressed(pixelgl.KeyD) {
			camPos.X += camSpeed * dt
		}
		if win.Pressed(pixelgl.KeyDown) || win.Pressed(pixelgl.KeyS) {
			camPos.Y -= camSpeed * dt
		}
		if win.Pressed(pixelgl.KeyUp) || win.Pressed(pixelgl.KeyW) {
			camPos.Y += camSpeed * dt
		}

		//	canvas.Clear(pixel.Alpha(0))
		win.Clear(colornames.Green)
		batch.Clear()

		batch.Draw(win)
		if b := box.Size(); b.Len() != 0 {
			if win.Pressed(pixelgl.KeyLeftControl) {
				if win.JustReleased(pixelgl.MouseButtonLeft) {
					box.Max = mouse
					box = box.Norm()
					log.Println("drawing rectangle:", box, currentThing)
					things = append(DeleteThings(things, box), rpg.DrawPatternObject(currentThing, rpg.O_TILE, box, 100)...)
				}
				if win.JustReleased(pixelgl.MouseButtonRight) {
					box.Max = mouse
					box = box.Norm()
					log.Println("drawing rectangle:", box, currentThing)
					things = append(DeleteThings(things, box), rpg.DrawPatternObject(currentThing, rpg.O_BLOCK, box, 100)...)

				}
			}
		}

		for i := range things {
			things[i].Draw(batch, spritesheet, spritemap)
			if highlight {
				color := rpg.TransparentRed
				if things[i].Type == rpg.O_TILE {
					color = rpg.TransparentBlue
				}
				things[i].Highlight(batch, color)
			}
			if things[i].Rect.Contains(mouse) {
				things[i].Highlight(batch, rpg.TransparentPurple)

			}

		}

		batch.Draw(win)

		// draw player spawn
		spritemap[182].Draw(win, IM.Scaled(ZV, 2).Moved(pixel.V(16, 16))) // incorrect offset

		// return cam
		win.SetMatrix(IM)
		spritemap[currentThing].Draw(win, IM.Scaled(ZV, 2).Moved(pixel.V(64, 64)).Moved(spritemap[0].Frame().Center()))
		text.Draw(win, IM.Moved(pixel.V(10, 10)))
		cursor.Draw(win, IM.Moved(win.MousePosition()).Moved(pixel.V(32, -32)))

		win.Update()

		select {
		default: //
		case <-second:
			//	log.Println("Offset:", offset)
			log.Println("Last DT", dt)
			log.Println("FPS:", frames)
			log.Printf("things: %v", len(things))
			//log.Printf("dynamic things: %v", len(world.DObjects))
			frames = 0
		}
	}
}

func main() {
	pixelgl.Run(run)
}

func DeleteThings(from []rpg.Object, at pixel.Rect) []rpg.Object {
	var cleaned []rpg.Object
	for _, o := range from {
		if !at.Contains(o.Loc) {
			cleaned = append(cleaned, o)
		}

	}
	return cleaned
}
<|endoftext|>