<commit_before>package nyb

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/url"
	"strings"
	"time"

	"github.com/hako/durafmt"
	"github.com/ugjka/dumbirc"
	"gopkg.in/ugjka/go-tz.v2/tz"
)

var nickChangeInterval = time.Second * 5

func (bot *Settings) addCallbacks() {
	irc := bot.IrcConn
	//On any message send a signal to ping timer to be ready

	irc.AddCallback(dumbirc.WELCOME, func(msg *dumbirc.Message) {
		if irc.Password != "" {
			confirmErr := fmt.Errorf("did not get identification confirmation")
			err := irc.WaitFor(func(m *dumbirc.Message) bool {
				return m.Command == dumbirc.NOTICE && strings.Contains(m.Content, "You are now identified for")
			},
				func() {},
				time.Second*30,
				confirmErr,
			)
			if err == confirmErr {
				log.Println(err)
				log.Println("trying to start the bot anyway")
			} else if err != nil {
				return
			}
		}
		irc.Join(bot.IrcChans)
		//Prevent early start
		bot.Do(func() {
			close(bot.start)
		})
	})
}

func (bot *Settings) addTriggers() {
	irc := bot.IrcConn
	//Trigger for !help
	stHelp := "%s: Query location: '%s <location>', Time in location: '%s !time <location>', Next zone: '%s !next', Last zone: '%s !last', Remaining: '%s !remaining', Source code: https://github.com/ugjka/newyearsbot"
	irc.AddTrigger(dumbirc.Trigger{
		Condition: func(msg *dumbirc.Message) bool {
			return msg.Command == dumbirc.PRIVMSG &&
				msg.Content == fmt.Sprintf("%s !help", bot.IrcTrigger)
		},
		Response: func(msg *dumbirc.Message) {
			log.Println("Querying !help...")
			irc.Reply(msg, fmt.Sprintf(stHelp, msg.Name, bot.IrcTrigger, bot.IrcTrigger, bot.IrcTrigger, bot.IrcTrigger, bot.IrcTrigger))
		},
	})
	//Trigger for !next
	irc.AddTrigger(dumbirc.Trigger{
		Condition: func(msg *dumbirc.Message) bool {
			return msg.Command == dumbirc.PRIVMSG &&
				msg.Content == fmt.Sprintf("%s !next", bot.IrcTrigger)
		},
		Response: func(msg *dumbirc.Message) {
			log.Println("Querying !next...")
			dur := time.Minute * time.Duration(bot.next.Offset*60)
			if timeNow().UTC().Add(dur).After(target) {
				irc.Reply(msg, fmt.Sprintf("No more next, %d is here AoE", target.Year()))
				return
			}
			humandur := durafmt.Parse(target.Sub(timeNow().UTC().Add(dur)))
			irc.Reply(msg, fmt.Sprintf("Next New Year in %s in %s",
				removeMilliseconds(humandur), bot.next))
		},
	})
	//Trigger for !last
	irc.AddTrigger(dumbirc.Trigger{
		Condition: func(msg *dumbirc.Message) bool {
			return msg.Command == dumbirc.PRIVMSG &&
				msg.Content == fmt.Sprintf("%s !last", bot.IrcTrigger)
		},
		Response: func(msg *dumbirc.Message) {
			log.Println("Querying !last...")
			dur := time.Minute * time.Duration(bot.last.Offset*60)
			humandur := durafmt.Parse(timeNow().UTC().Add(dur).Sub(target))
			if bot.last.Offset == -12 {
				humandur = durafmt.Parse(timeNow().UTC().Add(dur).Sub(target.AddDate(-1, 0, 0)))
			}
			irc.Reply(msg, fmt.Sprintf("Last New Year %s ago in %s",
				removeMilliseconds(humandur), bot.last))
		},
	})
	//Trigger for !remaining
	irc.AddTrigger(dumbirc.Trigger{
		Condition: func(msg *dumbirc.Message) bool {
			return msg.Command == dumbirc.PRIVMSG &&
				msg.Content == fmt.Sprintf("%s !remaining", bot.IrcTrigger)
		},
		Response: func(msg *dumbirc.Message) {
			log.Println("Querying !remaining...")
			ss := "s"
			if bot.remaining == 1 {
				ss = ""
			}
			irc.Reply(msg, fmt.Sprintf("%s: %d timezone%s remaining", msg.Name, bot.remaining, ss))
		},
	})
	//Trigger for time in location
	irc.AddTrigger(dumbirc.Trigger{
		Condition: func(msg *dumbirc.Message) bool {
			return msg.Command == dumbirc.PRIVMSG &&
				strings.HasPrefix(msg.Content, fmt.Sprintf("%s !time ", bot.IrcTrigger))
		},
		Response: func(msg *dumbirc.Message) {
			log.Println("Querying !time...")
			res, err := bot.getTime(msg.Content[len(bot.IrcTrigger)+7:])
			if err == errNoZone || err == errNoPlace {
				log.Println("Query error:", err)
				irc.Reply(msg, fmt.Sprintf("%s: %s", msg.Name, err))
				return
			}
			if err != nil {
				log.Println("Query error:", err)
				irc.Reply(msg, fmt.Sprintf("%s: Some error occurred!", msg.Name))
				return
			}
			irc.Reply(msg, res)
		},
	})
	//UTC
	irc.AddTrigger(dumbirc.Trigger{
		Condition: func(msg *dumbirc.Message) bool {
			return msg.Command == dumbirc.PRIVMSG &&
				msg.Content == fmt.Sprintf("%s !time", bot.IrcTrigger)
		},
		Response: func(msg *dumbirc.Message) {
			log.Println("Querying !time...")
			res := fmt.Sprintf("Time is %s", time.Now().UTC().Format("Mon Jan 2 15:04:05 -0700 MST 2006"))
			irc.Reply(msg, res)
		},
	})
	//Trigger for location queries
	irc.AddTrigger(dumbirc.Trigger{
		Condition: func(msg *dumbirc.Message) bool {
			return msg.Command == dumbirc.PRIVMSG &&
				!strings.Contains(msg.Content, "!time") &&
				!strings.Contains(msg.Content, "!next") &&
				!strings.Contains(msg.Content, "!last") &&
				!strings.Contains(msg.Content, "!help") &&
				!strings.Contains(msg.Content, "!remaining") &&
				strings.HasPrefix(msg.Content, fmt.Sprintf("%s ", bot.IrcTrigger))
		},
		Response: func(msg *dumbirc.Message) {
			tz, err := bot.getNewYear(msg.Content[len(bot.IrcTrigger)+1:])
			if err == errNoZone || err == errNoPlace {
				log.Println("Query error:", err)
				irc.Reply(msg, fmt.Sprintf("%s: %s", msg.Name, err))
				return
			}
			if err != nil {
				log.Println("Query error:", err)
				irc.Reply(msg, fmt.Sprintf("%s: Some error occurred!", msg.Name))
				return
			}
			irc.Reply(msg, fmt.Sprintf("%s: %s", msg.Name, tz))
		},
	})
}

var (
	errNoZone  = errors.New("couldn't get the timezone for that location")
	errNoPlace = errors.New("Couldn't find that place")
)

func (bot *Settings) getNominatimReqURL(location *string) string {
	maps := url.Values{}
	maps.Add("q", *location)
	maps.Add("format", "json")
	maps.Add("accept-language", "en")
	maps.Add("limit", "1")
	maps.Add("email", bot.Email)
	return bot.Nominatim + NominatimEndpoint + maps.Encode()
}

var stNewYearWillHappen = "New Year in %s will happen in %s"
var stNewYearHappenned = "New Year in %s happened %s ago"

func (bot *Settings) getTime(location string) (string, error) {
	log.Println("Querying location:", location)
	data, err := NominatimGetter(bot.getNominatimReqURL(&location))
	if err != nil {
		log.Println(err)
		return "", err
	}
	var res NominatimResults
	if err = json.Unmarshal(data, &res); err != nil {
		log.Println(err)
		return "", err
	}
	if len(res) == 0 {
		return "", errNoPlace
	}
	p := tz.Point{
		Lat: res[0].Lat,
		Lon: res[0].Lon,
	}
	tzid, err := tz.GetZone(p)
	if err != nil {
		return "", errNoZone
	}
	zone, err := time.LoadLocation(tzid[0])
	if err != nil {
		return "", errNoZone
	}
	address := res[0].DisplayName
	return fmt.Sprintf("Time in %s is %s", address, time.Now().In(zone).Format("Mon Jan 2 15:04:05 -0700 MST 2006")), nil
}

func (bot *Settings) getNewYear(location string) (string, error) {
	log.Println("Querying location:", location)
	data, err := NominatimGetter(bot.getNominatimReqURL(&location))
	if err != nil {
		log.Println(err)
		return "", err
	}
	var res NominatimResults
	if err = json.Unmarshal(data, &res); err != nil {
		log.Println(err)
		return "", err
	}
	if len(res) == 0 {
		return "", errNoPlace
	}
	p := tz.Point{
		Lat: res[0].Lat,
		Lon: res[0].Lon,
	}
	tzid, err := tz.GetZone(p)
	if err != nil {
		return "", errNoZone
	}
	zone, err := time.LoadLocation(tzid[0])
	if err != nil {
		return "", errNoZone
	}
	offset := time.Second * time.Duration(getOffset(target, zone))
	address := res[0].DisplayName

	if timeNow().UTC().Add(offset).Before(target) {
		humandur := durafmt.Parse(target.Sub(timeNow().UTC().Add(offset)))
		return fmt.Sprintf(stNewYearWillHappen, address, removeMilliseconds(humandur)), nil
	}
	humandur := durafmt.Parse(timeNow().UTC().Add(offset).Sub(target))
	return fmt.Sprintf(stNewYearHappenned, address, removeMilliseconds(humandur)), nil
}
<commit_msg>improve trigger checking<commit_after>package nyb

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/url"
	"strings"
	"time"

	"github.com/hako/durafmt"
	"github.com/ugjka/dumbirc"
	"gopkg.in/ugjka/go-tz.v2/tz"
)

var nickChangeInterval = time.Second * 5

func (bot *Settings) addCallbacks() {
	irc := bot.IrcConn
	//On any message send a signal to ping timer to be ready

	irc.AddCallback(dumbirc.WELCOME, func(msg *dumbirc.Message) {
		if irc.Password != "" {
			confirmErr := fmt.Errorf("did not get identification confirmation")
			err := irc.WaitFor(func(m *dumbirc.Message) bool {
				return m.Command == dumbirc.NOTICE && strings.Contains(m.Content, "You are now identified for")
			},
				func() {},
				time.Second*30,
				confirmErr,
			)
			if err == confirmErr {
				log.Println(err)
				log.Println("trying to start the bot anyway")
			} else if err != nil {
				return
			}
		}
		irc.Join(bot.IrcChans)
		//Prevent early start
		bot.Do(func() {
			close(bot.start)
		})
	})
}

func (bot *Settings) addTriggers() {
	irc := bot.IrcConn
	//Trigger for !help
	stHelp := "Query location: '%s <location>', Time in location: '%s !time <location>', Next zone: '%s !next', Last zone: '%s !last', Remaining: '%s !remaining'"
	stSource := "Source code: https://github.com/ugjka/newyearsbot"
	irc.AddTrigger(dumbirc.Trigger{
		Condition: func(msg *dumbirc.Message) bool {
			return msg.Command == dumbirc.PRIVMSG &&
				strings.HasPrefix(msg.Content, fmt.Sprintf("%s !help", bot.IrcTrigger)) ||
				msg.Content == fmt.Sprintf("%s help", bot.IrcTrigger)
		},
		Response: func(msg *dumbirc.Message) {
			log.Println("Querying !help...")
			irc.Reply(msg, fmt.Sprintf(stHelp+", "+stSource, bot.IrcTrigger, bot.IrcTrigger, bot.IrcTrigger, bot.IrcTrigger, bot.IrcTrigger))
		},
	})
	//Trigger for !next
	irc.AddTrigger(dumbirc.Trigger{
		Condition: func(msg *dumbirc.Message) bool {
			return msg.Command == dumbirc.PRIVMSG &&
				strings.HasPrefix(msg.Content, fmt.Sprintf("%s !next", bot.IrcTrigger)) ||
				msg.Content == fmt.Sprintf("%s next", bot.IrcTrigger)
		},
		Response: func(msg *dumbirc.Message) {
			log.Println("Querying !next...")
			dur := time.Minute * time.Duration(bot.next.Offset*60)
			if timeNow().UTC().Add(dur).After(target) {
				irc.Reply(msg, fmt.Sprintf("No more next, %d is here AoE", target.Year()))
				return
			}
			humandur := durafmt.Parse(target.Sub(timeNow().UTC().Add(dur)))
			irc.Reply(msg, fmt.Sprintf("Next New Year in %s in %s",
				removeMilliseconds(humandur), bot.next))
		},
	})
	//Trigger for !last
	irc.AddTrigger(dumbirc.Trigger{
		Condition: func(msg *dumbirc.Message) bool {
			return msg.Command == dumbirc.PRIVMSG &&
				strings.HasPrefix(msg.Content, fmt.Sprintf("%s !last", bot.IrcTrigger)) ||
				msg.Content == fmt.Sprintf("%s last", bot.IrcTrigger)
		},
		Response: func(msg *dumbirc.Message) {
			log.Println("Querying !last...")
			dur := time.Minute * time.Duration(bot.last.Offset*60)
			humandur := durafmt.Parse(timeNow().UTC().Add(dur).Sub(target))
			if bot.last.Offset == -12 {
				humandur = durafmt.Parse(timeNow().UTC().Add(dur).Sub(target.AddDate(-1, 0, 0)))
			}
			irc.Reply(msg, fmt.Sprintf("Last New Year %s ago in %s",
				removeMilliseconds(humandur), bot.last))
		},
	})
	//Trigger for !remaining
	irc.AddTrigger(dumbirc.Trigger{
		Condition: func(msg *dumbirc.Message) bool {
			return msg.Command == dumbirc.PRIVMSG &&
				strings.HasPrefix(msg.Content, fmt.Sprintf("%s !remaining", bot.IrcTrigger)) ||
				msg.Content == fmt.Sprintf("%s remaining", bot.IrcTrigger)
		},
		Response: func(msg *dumbirc.Message) {
			log.Println("Querying !remaining...")
			ss := "s"
			if bot.remaining == 1 {
				ss = ""
			}
			irc.Reply(msg, fmt.Sprintf("%s: %d timezone%s remaining", msg.Name, bot.remaining, ss))
		},
	})
	//Trigger for time in location
	irc.AddTrigger(dumbirc.Trigger{
		Condition: func(msg *dumbirc.Message) bool {
			return msg.Command == dumbirc.PRIVMSG &&
				strings.HasPrefix(msg.Content, fmt.Sprintf("%s !time ", bot.IrcTrigger))
		},
		Response: func(msg *dumbirc.Message) {
			log.Println("Querying !time...")
			res, err := bot.getTime(msg.Content[len(bot.IrcTrigger)+7:])
			if err == errNoZone || err == errNoPlace {
				log.Println("Query error:", err)
				irc.Reply(msg, fmt.Sprintf("%s: %s", msg.Name, err))
				return
			}
			if err != nil {
				log.Println("Query error:", err)
				irc.Reply(msg, fmt.Sprintf("%s: Some error occurred!", msg.Name))
				return
			}
			irc.Reply(msg, res)
		},
	})
	//UTC
	irc.AddTrigger(dumbirc.Trigger{
		Condition: func(msg *dumbirc.Message) bool {
			return msg.Command == dumbirc.PRIVMSG &&
				msg.Content == fmt.Sprintf("%s !time", bot.IrcTrigger)
		},
		Response: func(msg *dumbirc.Message) {
			log.Println("Querying !time...")
			res := fmt.Sprintf("Time is %s", time.Now().UTC().Format("Mon Jan 2 15:04:05 -0700 MST 2006"))
			irc.Reply(msg, res)
		},
	})

	//Trigger for incorrect querries
	irc.AddTrigger(dumbirc.Trigger{
		Condition: func(msg *dumbirc.Message) bool {
			return msg.Command == dumbirc.PRIVMSG &&
				msg.Content != fmt.Sprintf("%s !time", bot.IrcTrigger) &&
				!strings.HasPrefix(msg.Content, fmt.Sprintf("%s !time ", bot.IrcTrigger)) &&
				!strings.HasPrefix(msg.Content, fmt.Sprintf("%s !next", bot.IrcTrigger)) &&
				!strings.HasPrefix(msg.Content, fmt.Sprintf("%s !last", bot.IrcTrigger)) &&
				!strings.HasPrefix(msg.Content, fmt.Sprintf("%s !help", bot.IrcTrigger)) &&
				!strings.HasPrefix(msg.Content, fmt.Sprintf("%s !remaining", bot.IrcTrigger)) &&
				strings.HasPrefix(msg.Content, fmt.Sprintf("%s !", bot.IrcTrigger))
		},
		Response: func(msg *dumbirc.Message) {
			irc.Reply(msg, fmt.Sprintf("Invalid command, valid commands are: "+stHelp, bot.IrcTrigger, bot.IrcTrigger, bot.IrcTrigger, bot.IrcTrigger, bot.IrcTrigger))
		},
	})

	//Trigger for location queries
	irc.AddTrigger(dumbirc.Trigger{
		Condition: func(msg *dumbirc.Message) bool {
			return msg.Command == dumbirc.PRIVMSG &&
				!strings.HasPrefix(msg.Content, fmt.Sprintf("%s !", bot.IrcTrigger)) &&
				msg.Content != fmt.Sprintf("%s next", bot.IrcTrigger) &&
				msg.Content != fmt.Sprintf("%s last", bot.IrcTrigger) &&
				msg.Content != fmt.Sprintf("%s help", bot.IrcTrigger) &&
				msg.Content != fmt.Sprintf("%s remaining", bot.IrcTrigger) &&
				strings.HasPrefix(msg.Content, fmt.Sprintf("%s ", bot.IrcTrigger))
		},
		Response: func(msg *dumbirc.Message) {
			tz, err := bot.getNewYear(msg.Content[len(bot.IrcTrigger)+1:])
			if err == errNoZone || err == errNoPlace {
				log.Println("Query error:", err)
				irc.Reply(msg, fmt.Sprintf("%s: %s", msg.Name, err))
				return
			}
			if err != nil {
				log.Println("Query error:", err)
				irc.Reply(msg, fmt.Sprintf("%s: Some error occurred!", msg.Name))
				return
			}
			irc.Reply(msg, fmt.Sprintf("%s: %s", msg.Name, tz))
		},
	})
}

var (
	errNoZone  = errors.New("couldn't get the timezone for that location")
	errNoPlace = errors.New("Couldn't find that place")
)

func (bot *Settings) getNominatimReqURL(location *string) string {
	maps := url.Values{}
	maps.Add("q", *location)
	maps.Add("format", "json")
	maps.Add("accept-language", "en")
	maps.Add("limit", "1")
	maps.Add("email", bot.Email)
	return bot.Nominatim + NominatimEndpoint + maps.Encode()
}

var stNewYearWillHappen = "New Year in %s will happen in %s"
var stNewYearHappenned = "New Year in %s happened %s ago"

func (bot *Settings) getTime(location string) (string, error) {
	log.Println("Querying location:", location)
	data, err := NominatimGetter(bot.getNominatimReqURL(&location))
	if err != nil {
		log.Println(err)
		return "", err
	}
	var res NominatimResults
	if err = json.Unmarshal(data, &res); err != nil {
		log.Println(err)
		return "", err
	}
	if len(res) == 0 {
		return "", errNoPlace
	}
	p := tz.Point{
		Lat: res[0].Lat,
		Lon: res[0].Lon,
	}
	tzid, err := tz.GetZone(p)
	if err != nil {
		return "", errNoZone
	}
	zone, err := time.LoadLocation(tzid[0])
	if err != nil {
		return "", errNoZone
	}
	address := res[0].DisplayName
	return fmt.Sprintf("Time in %s is %s", address, time.Now().In(zone).Format("Mon Jan 2 15:04:05 -0700 MST 2006")), nil
}

func (bot *Settings) getNewYear(location string) (string, error) {
	log.Println("Querying location:", location)
	data, err := NominatimGetter(bot.getNominatimReqURL(&location))
	if err != nil {
		log.Println(err)
		return "", err
	}
	var res NominatimResults
	if err = json.Unmarshal(data, &res); err != nil {
		log.Println(err)
		return "", err
	}
	if len(res) == 0 {
		return "", errNoPlace
	}
	p := tz.Point{
		Lat: res[0].Lat,
		Lon: res[0].Lon,
	}
	tzid, err := tz.GetZone(p)
	if err != nil {
		return "", errNoZone
	}
	zone, err := time.LoadLocation(tzid[0])
	if err != nil {
		return "", errNoZone
	}
	offset := time.Second * time.Duration(getOffset(target, zone))
	address := res[0].DisplayName

	if timeNow().UTC().Add(offset).Before(target) {
		humandur := durafmt.Parse(target.Sub(timeNow().UTC().Add(offset)))
		return fmt.Sprintf(stNewYearWillHappen, address, removeMilliseconds(humandur)), nil
	}
	humandur := durafmt.Parse(timeNow().UTC().Add(offset).Sub(target))
	return fmt.Sprintf(stNewYearHappenned, address, removeMilliseconds(humandur)), nil
}
<|endoftext|>


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

// The oauth package provides support for making
// OAuth2-authenticated HTTP requests.
//
// Example usage:
//
//	// Specify your configuration. (typically as a global variable)
//	var config = &oauth.Config{
//		ClientId:     YOUR_CLIENT_ID,
//		ClientSecret: YOUR_CLIENT_SECRET,
//		Scope:        "https://www.googleapis.com/auth/buzz",
//		AuthURL:      "https://accounts.google.com/o/oauth2/auth",
//		TokenURL:     "https://accounts.google.com/o/oauth2/token",
//		RedirectURL:  "http://you.example.org/handler",
//	}
//
//	// A landing page redirects to the OAuth provider to get the auth code.
//	func landing(w http.ResponseWriter, r *http.Request) {
//		http.Redirect(w, r, config.AuthCodeURL("foo"), http.StatusFound)
//	}
//
//	// The user will be redirected back to this handler, that takes the
//	// "code" query parameter and Exchanges it for an access token.
//	func handler(w http.ResponseWriter, r *http.Request) {
//		t := &oauth.Transport{Config: config}
//		t.Exchange(r.FormValue("code"))
//		// The Transport now has a valid Token. Create an *http.Client
//		// with which we can make authenticated API requests.
//		c := t.Client()
//		c.Post(...)
//		// ...
//		// btw, r.FormValue("state") == "foo"
//	}
//
package oauth

// TODO(adg): A means of automatically saving credentials when updated.

import (
	"http"
	"json"
	"os"
	"time"
)

// Config is the configuration of an OAuth consumer.
type Config struct {
	ClientId     string
	ClientSecret string
	Scope        string
	AuthURL      string
	TokenURL     string
	RedirectURL  string // Defaults to out-of-band mode if empty.
}

func (c *Config) redirectURL() string {
	if c.RedirectURL != "" {
		return c.RedirectURL
	}
	return "oob"
}

// Token contains an end-user's tokens.
// This is the data you must store to persist authentication.
type Token struct {
	AccessToken  string "access_token"
	RefreshToken string "refresh_token"
	TokenExpiry  int64  "expires_in"
}

// Transport implements http.RoundTripper. When configured with a valid
// Config and Token it can be used to make authenticated HTTP requests.
//
//	t := &oauth.Transport{config}
//      t.Exchange(code)
//      // t now contains a valid Token
//	r, _, err := t.Client().Get("http://example.org/url/requiring/auth")
//
// It will automatically refresh the Token if it can,
// updating the supplied Token in place.
type Transport struct {
	*Config
	*Token

	// Transport is the HTTP transport to use when making requests.
	// It will default to http.DefaultTransport if nil.
	// (It should never be an oauth.Transport.)
	Transport http.RoundTripper
}

// Client returns an *http.Client that makes OAuth-authenticated requests.
func (t *Transport) Client() *http.Client {
	return &http.Client{Transport: t}
}

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

// AuthCodeURL returns a URL that the end-user should be redirected to,
// so that they may obtain an authorization code.
func (c *Config) AuthCodeURL(state string) string {
	url, err := http.ParseURL(c.AuthURL)
	if err != nil {
		panic("AuthURL malformed: " + err.String())
	}
	q := http.EncodeQuery(map[string][]string{
		"response_type": {"code"},
		"client_id":     {c.ClientId},
		"redirect_uri":  {c.redirectURL()},
		"scope":         {c.Scope},
		"state":         {state},
	})
	if url.RawQuery == "" {
		url.RawQuery = q
	} else {
		url.RawQuery += "&" + q
	}
	return url.String()
}

// Exchange takes a code and gets access Token from the remote server.
func (t *Transport) Exchange(code string) (tok *Token, err os.Error) {
	if t.Config == nil {
		return nil, os.NewError("no Config supplied")
	}
	tok = new(Token)
	err = t.updateToken(tok, map[string]string{
		"grant_type":   "authorization_code",
		"redirect_uri": t.redirectURL(),
		"scope":        t.Scope,
		"code":         code,
	})
	if err == nil {
		t.Token = tok
	}
	return
}

// RoundTrip executes a single HTTP transaction using the Transport's
// Token as authorization headers.
func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err os.Error) {
	if t.Config == nil {
		return nil, os.NewError("no Config supplied")
	}
	if t.Token == nil {
		return nil, os.NewError("no Token supplied")
	}

	// Make the HTTP request
	req.Header.Set("Authorization", "OAuth "+t.AccessToken)
	if resp, err = t.transport().RoundTrip(req); err != nil {
		return
	}

	// Refresh credentials if they're stale and try again
	if resp.StatusCode == 401 {
		if err = t.refresh(); err != nil {
			return
		}
		resp, err = t.transport().RoundTrip(req)
	}

	return
}

func (t *Transport) refresh() os.Error {
	return t.updateToken(t.Token, map[string]string{
		"grant_type":    "refresh_token",
		"refresh_token": t.RefreshToken,
	})
}

func (t *Transport) updateToken(tok *Token, form map[string]string) os.Error {
	form["client_id"] = t.ClientId
	form["client_secret"] = t.ClientSecret
	r, err := (&http.Client{Transport: t.transport()}).PostForm(t.TokenURL, form)
	if err != nil {
		return err
	}
	defer r.Body.Close()
	if r.StatusCode != 200 {
		return os.NewError("invalid response: " + r.Status)
	}
	if err = json.NewDecoder(r.Body).Decode(tok); err != nil {
		return err
	}
	if tok.TokenExpiry != 0 {
		tok.TokenExpiry = time.Seconds() + tok.TokenExpiry
	}
	return nil
}
<commit_msg>update to latest http package'<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The oauth package provides support for making
// OAuth2-authenticated HTTP requests.
//
// Example usage:
//
//	// Specify your configuration. (typically as a global variable)
//	var config = &oauth.Config{
//		ClientId:     YOUR_CLIENT_ID,
//		ClientSecret: YOUR_CLIENT_SECRET,
//		Scope:        "https://www.googleapis.com/auth/buzz",
//		AuthURL:      "https://accounts.google.com/o/oauth2/auth",
//		TokenURL:     "https://accounts.google.com/o/oauth2/token",
//		RedirectURL:  "http://you.example.org/handler",
//	}
//
//	// A landing page redirects to the OAuth provider to get the auth code.
//	func landing(w http.ResponseWriter, r *http.Request) {
//		http.Redirect(w, r, config.AuthCodeURL("foo"), http.StatusFound)
//	}
//
//	// The user will be redirected back to this handler, that takes the
//	// "code" query parameter and Exchanges it for an access token.
//	func handler(w http.ResponseWriter, r *http.Request) {
//		t := &oauth.Transport{Config: config}
//		t.Exchange(r.FormValue("code"))
//		// The Transport now has a valid Token. Create an *http.Client
//		// with which we can make authenticated API requests.
//		c := t.Client()
//		c.Post(...)
//		// ...
//		// btw, r.FormValue("state") == "foo"
//	}
//
package oauth

// TODO(adg): A means of automatically saving credentials when updated.

import (
	"http"
	"json"
	"os"
	"time"
)

// Config is the configuration of an OAuth consumer.
type Config struct {
	ClientId     string
	ClientSecret string
	Scope        string
	AuthURL      string
	TokenURL     string
	RedirectURL  string // Defaults to out-of-band mode if empty.
}

func (c *Config) redirectURL() string {
	if c.RedirectURL != "" {
		return c.RedirectURL
	}
	return "oob"
}

// Token contains an end-user's tokens.
// This is the data you must store to persist authentication.
type Token struct {
	AccessToken  string "access_token"
	RefreshToken string "refresh_token"
	TokenExpiry  int64  "expires_in"
}

// Transport implements http.RoundTripper. When configured with a valid
// Config and Token it can be used to make authenticated HTTP requests.
//
//	t := &oauth.Transport{config}
//      t.Exchange(code)
//      // t now contains a valid Token
//	r, _, err := t.Client().Get("http://example.org/url/requiring/auth")
//
// It will automatically refresh the Token if it can,
// updating the supplied Token in place.
type Transport struct {
	*Config
	*Token

	// Transport is the HTTP transport to use when making requests.
	// It will default to http.DefaultTransport if nil.
	// (It should never be an oauth.Transport.)
	Transport http.RoundTripper
}

// Client returns an *http.Client that makes OAuth-authenticated requests.
func (t *Transport) Client() *http.Client {
	return &http.Client{Transport: t}
}

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

// AuthCodeURL returns a URL that the end-user should be redirected to,
// so that they may obtain an authorization code.
func (c *Config) AuthCodeURL(state string) string {
	url, err := http.ParseURL(c.AuthURL)
	if err != nil {
		panic("AuthURL malformed: " + err.String())
	}
	q := http.Values{
		"response_type": {"code"},
		"client_id":     {c.ClientId},
		"redirect_uri":  {c.redirectURL()},
		"scope":         {c.Scope},
		"state":         {state},
	}.Encode()
	if url.RawQuery == "" {
		url.RawQuery = q
	} else {
		url.RawQuery += "&" + q
	}
	return url.String()
}

// Exchange takes a code and gets access Token from the remote server.
func (t *Transport) Exchange(code string) (tok *Token, err os.Error) {
	if t.Config == nil {
		return nil, os.NewError("no Config supplied")
	}
	tok = new(Token)
	err = t.updateToken(tok, http.Values{
		"grant_type":   {"authorization_code"},
		"redirect_uri": {t.redirectURL()},
		"scope":        {t.Scope},
		"code":         {code},
	})
	if err == nil {
		t.Token = tok
	}
	return
}

// RoundTrip executes a single HTTP transaction using the Transport's
// Token as authorization headers.
func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err os.Error) {
	if t.Config == nil {
		return nil, os.NewError("no Config supplied")
	}
	if t.Token == nil {
		return nil, os.NewError("no Token supplied")
	}

	// Make the HTTP request
	req.Header.Set("Authorization", "OAuth "+t.AccessToken)
	if resp, err = t.transport().RoundTrip(req); err != nil {
		return
	}

	// Refresh credentials if they're stale and try again
	if resp.StatusCode == 401 {
		if err = t.refresh(); err != nil {
			return
		}
		resp, err = t.transport().RoundTrip(req)
	}

	return
}

func (t *Transport) refresh() os.Error {
	return t.updateToken(t.Token, http.Values{
		"grant_type":    {"refresh_token"},
		"refresh_token": {t.RefreshToken},
	})
}

func (t *Transport) updateToken(tok *Token, v http.Values) os.Error {
	v.Set("client_id", t.ClientId)
	v.Set("client_secret", t.ClientSecret)
	r, err := (&http.Client{Transport: t.transport()}).PostForm(t.TokenURL, v)
	if err != nil {
		return err
	}
	defer r.Body.Close()
	if r.StatusCode != 200 {
		return os.NewError("invalid response: " + r.Status)
	}
	if err = json.NewDecoder(r.Body).Decode(tok); err != nil {
		return err
	}
	if tok.TokenExpiry != 0 {
		tok.TokenExpiry = time.Seconds() + tok.TokenExpiry
	}
	return nil
}
<|endoftext|>


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

package docker

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/dotcloud/docker"
	dclient "github.com/fsouza/go-dockerclient"
	"github.com/globocom/config"
	"github.com/globocom/docker-cluster/cluster"
	"github.com/globocom/tsuru/fs"
	"github.com/globocom/tsuru/log"
	"github.com/globocom/tsuru/provision"
	"io"
	"labix.org/v2/mgo/bson"
	"strings"
)

var dockerCluster *cluster.Cluster

func init() {
	dockerCluster, _ = cluster.New(
		cluster.Node{ID: "server", Address: "http://localhost:4243"},
	)
}

var fsystem fs.Fs

func filesystem() fs.Fs {
	if fsystem == nil {
		fsystem = fs.OsFs{}
	}
	return fsystem
}

// runCmd executes commands and log the given stdout and stderror.
func runCmd(cmd string, args ...string) (string, error) {
	out := bytes.Buffer{}
	err := executor().Execute(cmd, args, nil, &out, &out)
	log.Printf("running the cmd: %s with the args: %s", cmd, args)
	if err != nil {
		return "", &cmdError{cmd: cmd, args: args, err: err, out: out.String()}
	}
	return out.String(), nil
}

func getPort() (string, error) {
	return config.GetString("docker:run-cmd:port")
}

type container struct {
	ID       string `bson:"_id"`
	AppName  string
	Type     string
	IP       string
	Port     string
	HostPort string
	Status   string
	Version  string
	Image    string
}

func (c *container) getAddress() string {
	hostAddr, err := config.Get("docker:host-address")
	if err != nil {
		log.Printf("Failed to obtain container address: %s", err.Error())
		return ""
	}
	return fmt.Sprintf("http://%s:%s", hostAddr, c.HostPort)
}

// newContainer creates a new container in Docker and stores it in the database.
//
// TODO (flaviamissi): make it atomic
func newContainer(app provision.App, imageId string, commands []string) (*container, error) {
	appName := app.GetName()
	c := container{
		AppName: appName,
		Type:    app.GetPlatform(),
	}
	err := c.create(imageId, commands)
	if err != nil {
		log.Printf("Error creating container for the app %q: %s", appName, err)
		return nil, err
	}
	return &c, nil
}

// hostPort returns the host port mapped for the container.
func (c *container) hostPort() (string, error) {
	if c.Port == "" {
		return "", errors.New("Container does not contain any mapped port")
	}
	dockerContainer, err := dockerCluster.InspectContainer(c.ID)
	if err != nil {
		return "", err
	}
	if dockerContainer.NetworkSettings != nil {
		mappedPorts := dockerContainer.NetworkSettings.PortMapping
		if port, ok := mappedPorts[c.Port]; ok {
			return port, nil
		}
	}
	return "", fmt.Errorf("Container port %s is not mapped to any host port", c.Port)
}

// ip returns the ip for the container.
func (c *container) ip() (string, error) {
	dockerContainer, err := dockerCluster.InspectContainer(c.ID)
	if err != nil {
		return "", err
	}
	if dockerContainer.NetworkSettings == nil {
		msg := "Error when getting container information. NetworkSettings is missing."
		log.Print(msg)
		return "", errors.New(msg)
	}
	instanceIP := dockerContainer.NetworkSettings.IPAddress
	if instanceIP == "" {
		msg := "error: Can't get ipaddress..."
		log.Print(msg)
		return "", errors.New(msg)
	}
	log.Printf("Instance IpAddress: %s", instanceIP)
	return instanceIP, nil
}

// create creates a docker container, stores it on the database and adds a route to it.
//
// It receives the related application in order to choose the correct
// docker image and the repository to pass to the script that will take
// care of the deploy, and a function to generate the correct command ran by
// docker, which might be to deploy a container or to run and expose a
// container for an application.
func (c *container) create(imageId string, commands []string) error {
	port, err := getPort()
	if err != nil {
		return err
	}
	user, err := config.GetString("docker:ssh:user")
	if err != nil {
		return err
	}
	config := docker.Config{
		Image:        imageId,
		Cmd:          commands,
		PortSpecs:    []string{port},
		User:         user,
		AttachStdin:  false,
		AttachStdout: false,
		AttachStderr: false,
	}
	_, cont, err := dockerCluster.CreateContainer(&config)
	if err != nil {
		return err
	}
	c.ID = cont.ID
	c.Port = port
	ip, err := c.ip()
	if err != nil {
		return err
	}
	c.IP = ip
	hostPort, err := c.hostPort()
	if err != nil {
		return err
	}
	c.HostPort = hostPort
	c.Status = "created"
	coll := collection()
	defer coll.Database.Session.Close()
	if err := coll.Insert(c); err != nil {
		log.Print(err)
		return err
	}
	r, err := getRouter()
	if err != nil {
		return err
	}
	return r.AddRoute(c.AppName, c.getAddress())
}

func (c *container) setStatus(status string) error {
	c.Status = status
	coll := collection()
	defer coll.Database.Session.Close()
	return coll.UpdateId(c.ID, c)
}

func (c *container) setImage(imageId string) error {
	c.Image = imageId
	coll := collection()
	defer coll.Database.Session.Close()
	return coll.UpdateId(c.ID, c)
}

func deploy(app provision.App, version string, w io.Writer) (string, error) {
	commands, err := deployCmds(app, version)
	if err != nil {
		return "", err
	}
	imageId := getImage(app)
	c, err := newContainer(app, imageId, commands)
	if err != nil {
		return "", err
	}
	for {
		result, err := c.stopped()
		if err != nil {
			return "", err
		}
		if result {
			break
		}
	}
	err = c.logs(w)
	if err != nil {
		return "", err
	}
	imageId, err = c.commit()
	if err != nil {
		return "", err
	}
	err = c.remove()
	if err != nil {
		return "", err
	}
	return imageId, nil
}

func start(app provision.App, imageId string, w io.Writer) (*container, error) {
	commands, err := runCmds()
	if err != nil {
		return nil, err
	}
	c, err := newContainer(app, imageId, commands)
	if err != nil {
		return nil, err
	}
	err = c.setImage(imageId)
	if err != nil {
		return nil, err
	}
	err = c.setStatus("running")
	if err != nil {
		return nil, err
	}
	return c, nil
}

// remove removes a docker container.
func (c *container) remove() error {
	address := c.getAddress()
	log.Printf("Removing container %s from docker", c.ID)
	err := dockerCluster.RemoveContainer(c.ID)
	if err != nil {
		log.Printf("Failed to remove container from docker: %s", err)
		return err
	}
	runCmd("ssh-keygen", "-R", c.IP)
	log.Printf("Removing container %s from database", c.ID)
	coll := collection()
	defer coll.Database.Session.Close()
	if err := coll.RemoveId(c.ID); err != nil {
		log.Printf("Failed to remove container from database: %s", err.Error())
		return err
	}
	r, err := getRouter()
	if err != nil {
		log.Printf("Failed to obtain router: %s", err.Error())
		return err
	}
	if err := r.RemoveRoute(c.AppName, address); err != nil {
		log.Printf("Failed to remove route: %s", err.Error())
		return err
	}
	return nil
}

func (c *container) ssh(stdout, stderr io.Writer, cmd string, args ...string) error {
	stderr = &filter{w: stderr, content: []byte("unable to resolve host")}
	user, err := config.GetString("docker:ssh:user")
	if err != nil {
		return err
	}
	sshArgs := []string{c.IP, "-l", user, "-o", "StrictHostKeyChecking no"}
	if keyFile, err := config.GetString("docker:ssh:private-key"); err == nil {
		sshArgs = append(sshArgs, "-i", keyFile)
	}
	sshArgs = append(sshArgs, "--", cmd)
	sshArgs = append(sshArgs, args...)
	return executor().Execute("ssh", sshArgs, nil, stdout, stderr)
}

// commit commits an image in docker based in the container
func (c *container) commit() (string, error) {
	opts := dclient.CommitContainerOptions{Container: c.ID}
	image, err := dockerCluster.CommitContainer(opts)
	if err != nil {
		log.Printf("Could not commit docker image: %s", err.Error())
		return "", err
	}
	return image.ID, nil
}

// stopped returns true if the container is stopped.
func (c *container) stopped() (bool, error) {
	dockerContainer, err := dockerCluster.InspectContainer(c.ID)
	if err != nil {
		return true, err
	}
	running := dockerContainer.State.Running
	return !running, nil
}

// logs returns logs for the container.
func (c *container) logs(w io.Writer) error {
	opts := dclient.AttachToContainerOptions{
		Container:    c.ID,
		Logs:         true,
		OutputStream: w,
	}
	return dockerCluster.AttachToContainer(opts)
}

func getContainer(id string) (*container, error) {
	var c container
	coll := collection()
	defer coll.Database.Session.Close()
	err := coll.Find(bson.M{"_id": id}).One(&c)
	if err != nil {
		return nil, err
	}
	return &c, nil
}

func listAppContainers(appName string) ([]container, error) {
	var containers []container
	err := collection().Find(bson.M{"appname": appName}).All(&containers)
	return containers, err
}

// getImage returns the image name or id from an app.
func getImage(app provision.App) string {
	var c container
	collection().Find(bson.M{"appname": app.GetName()}).One(&c)
	if c.Image != "" {
		return c.Image
	}
	repoNamespace, err := config.GetString("docker:repository-namespace")
	if err != nil {
		return ""
	}
	return fmt.Sprintf("%s/%s", repoNamespace, app.GetPlatform())
}

// removeImage removes an image from docker registry
func removeImage(imageId string) error {
	return dockerCluster.RemoveImage(imageId)
}

type cmdError struct {
	cmd  string
	args []string
	err  error
	out  string
}

func (e *cmdError) Error() string {
	command := e.cmd + " " + strings.Join(e.args, " ")
	return fmt.Sprintf("Failed to run command %q (%s): %s.", command, e.err, e.out)
}
<commit_msg>docker: igoring errors when ip or port is none.<commit_after>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package docker

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/dotcloud/docker"
	dclient "github.com/fsouza/go-dockerclient"
	"github.com/globocom/config"
	"github.com/globocom/docker-cluster/cluster"
	"github.com/globocom/tsuru/fs"
	"github.com/globocom/tsuru/log"
	"github.com/globocom/tsuru/provision"
	"io"
	"labix.org/v2/mgo/bson"
	"strings"
)

var dockerCluster *cluster.Cluster

func init() {
	dockerCluster, _ = cluster.New(
		cluster.Node{ID: "server", Address: "http://localhost:4243"},
	)
}

var fsystem fs.Fs

func filesystem() fs.Fs {
	if fsystem == nil {
		fsystem = fs.OsFs{}
	}
	return fsystem
}

// runCmd executes commands and log the given stdout and stderror.
func runCmd(cmd string, args ...string) (string, error) {
	out := bytes.Buffer{}
	err := executor().Execute(cmd, args, nil, &out, &out)
	log.Printf("running the cmd: %s with the args: %s", cmd, args)
	if err != nil {
		return "", &cmdError{cmd: cmd, args: args, err: err, out: out.String()}
	}
	return out.String(), nil
}

func getPort() (string, error) {
	return config.GetString("docker:run-cmd:port")
}

type container struct {
	ID       string `bson:"_id"`
	AppName  string
	Type     string
	IP       string
	Port     string
	HostPort string
	Status   string
	Version  string
	Image    string
}

func (c *container) getAddress() string {
	hostAddr, err := config.Get("docker:host-address")
	if err != nil {
		log.Printf("Failed to obtain container address: %s", err.Error())
		return ""
	}
	return fmt.Sprintf("http://%s:%s", hostAddr, c.HostPort)
}

// newContainer creates a new container in Docker and stores it in the database.
//
// TODO (flaviamissi): make it atomic
func newContainer(app provision.App, imageId string, commands []string) (*container, error) {
	appName := app.GetName()
	c := container{
		AppName: appName,
		Type:    app.GetPlatform(),
	}
	err := c.create(imageId, commands)
	if err != nil {
		log.Printf("Error creating container for the app %q: %s", appName, err)
		return nil, err
	}
	return &c, nil
}

// hostPort returns the host port mapped for the container.
func (c *container) hostPort() (string, error) {
	if c.Port == "" {
		return "", errors.New("Container does not contain any mapped port")
	}
	dockerContainer, err := dockerCluster.InspectContainer(c.ID)
	if err != nil {
		return "", err
	}
	if dockerContainer.NetworkSettings != nil {
		mappedPorts := dockerContainer.NetworkSettings.PortMapping
		if port, ok := mappedPorts[c.Port]; ok {
			return port, nil
		}
	}
	return "", fmt.Errorf("Container port %s is not mapped to any host port", c.Port)
}

// ip returns the ip for the container.
func (c *container) ip() (string, error) {
	dockerContainer, err := dockerCluster.InspectContainer(c.ID)
	if err != nil {
		return "", err
	}
	if dockerContainer.NetworkSettings == nil {
		msg := "Error when getting container information. NetworkSettings is missing."
		log.Print(msg)
		return "", errors.New(msg)
	}
	instanceIP := dockerContainer.NetworkSettings.IPAddress
	if instanceIP == "" {
		msg := "error: Can't get ipaddress..."
		log.Print(msg)
		return "", errors.New(msg)
	}
	log.Printf("Instance IpAddress: %s", instanceIP)
	return instanceIP, nil
}

// create creates a docker container, stores it on the database and adds a route to it.
//
// It receives the related application in order to choose the correct
// docker image and the repository to pass to the script that will take
// care of the deploy, and a function to generate the correct command ran by
// docker, which might be to deploy a container or to run and expose a
// container for an application.
func (c *container) create(imageId string, commands []string) error {
	port, err := getPort()
	if err != nil {
		return err
	}
	user, err := config.GetString("docker:ssh:user")
	if err != nil {
		return err
	}
	config := docker.Config{
		Image:        imageId,
		Cmd:          commands,
		PortSpecs:    []string{port},
		User:         user,
		AttachStdin:  false,
		AttachStdout: false,
		AttachStderr: false,
	}
	_, cont, err := dockerCluster.CreateContainer(&config)
	if err != nil {
		return err
	}
	c.ID = cont.ID
	c.Port = port
	ip, err := c.ip()
	// if err != nil {
	// 	return err
	// }
	c.IP = ip
	hostPort, err := c.hostPort()
	// if err != nil {
	// 	return err
	// }
	c.HostPort = hostPort
	c.Status = "created"
	coll := collection()
	defer coll.Database.Session.Close()
	if err := coll.Insert(c); err != nil {
		log.Print(err)
		return err
	}
	r, err := getRouter()
	if err != nil {
		return err
	}
	return r.AddRoute(c.AppName, c.getAddress())
}

func (c *container) setStatus(status string) error {
	c.Status = status
	coll := collection()
	defer coll.Database.Session.Close()
	return coll.UpdateId(c.ID, c)
}

func (c *container) setImage(imageId string) error {
	c.Image = imageId
	coll := collection()
	defer coll.Database.Session.Close()
	return coll.UpdateId(c.ID, c)
}

func deploy(app provision.App, version string, w io.Writer) (string, error) {
	commands, err := deployCmds(app, version)
	if err != nil {
		return "", err
	}
	imageId := getImage(app)
	c, err := newContainer(app, imageId, commands)
	if err != nil {
		return "", err
	}
	for {
		result, err := c.stopped()
		if err != nil {
			return "", err
		}
		if result {
			break
		}
	}
	err = c.logs(w)
	if err != nil {
		return "", err
	}
	imageId, err = c.commit()
	if err != nil {
		return "", err
	}
	err = c.remove()
	if err != nil {
		return "", err
	}
	return imageId, nil
}

func start(app provision.App, imageId string, w io.Writer) (*container, error) {
	commands, err := runCmds()
	if err != nil {
		return nil, err
	}
	c, err := newContainer(app, imageId, commands)
	if err != nil {
		return nil, err
	}
	err = c.setImage(imageId)
	if err != nil {
		return nil, err
	}
	err = c.setStatus("running")
	if err != nil {
		return nil, err
	}
	return c, nil
}

// remove removes a docker container.
func (c *container) remove() error {
	address := c.getAddress()
	log.Printf("Removing container %s from docker", c.ID)
	err := dockerCluster.RemoveContainer(c.ID)
	if err != nil {
		log.Printf("Failed to remove container from docker: %s", err)
		return err
	}
	runCmd("ssh-keygen", "-R", c.IP)
	log.Printf("Removing container %s from database", c.ID)
	coll := collection()
	defer coll.Database.Session.Close()
	if err := coll.RemoveId(c.ID); err != nil {
		log.Printf("Failed to remove container from database: %s", err.Error())
		return err
	}
	r, err := getRouter()
	if err != nil {
		log.Printf("Failed to obtain router: %s", err.Error())
		return err
	}
	if err := r.RemoveRoute(c.AppName, address); err != nil {
		log.Printf("Failed to remove route: %s", err.Error())
		return err
	}
	return nil
}

func (c *container) ssh(stdout, stderr io.Writer, cmd string, args ...string) error {
	stderr = &filter{w: stderr, content: []byte("unable to resolve host")}
	user, err := config.GetString("docker:ssh:user")
	if err != nil {
		return err
	}
	sshArgs := []string{c.IP, "-l", user, "-o", "StrictHostKeyChecking no"}
	if keyFile, err := config.GetString("docker:ssh:private-key"); err == nil {
		sshArgs = append(sshArgs, "-i", keyFile)
	}
	sshArgs = append(sshArgs, "--", cmd)
	sshArgs = append(sshArgs, args...)
	return executor().Execute("ssh", sshArgs, nil, stdout, stderr)
}

// commit commits an image in docker based in the container
func (c *container) commit() (string, error) {
	opts := dclient.CommitContainerOptions{Container: c.ID}
	image, err := dockerCluster.CommitContainer(opts)
	if err != nil {
		log.Printf("Could not commit docker image: %s", err.Error())
		return "", err
	}
	return image.ID, nil
}

// stopped returns true if the container is stopped.
func (c *container) stopped() (bool, error) {
	dockerContainer, err := dockerCluster.InspectContainer(c.ID)
	if err != nil {
		return true, err
	}
	running := dockerContainer.State.Running
	return !running, nil
}

// logs returns logs for the container.
func (c *container) logs(w io.Writer) error {
	opts := dclient.AttachToContainerOptions{
		Container:    c.ID,
		Logs:         true,
		OutputStream: w,
	}
	return dockerCluster.AttachToContainer(opts)
}

func getContainer(id string) (*container, error) {
	var c container
	coll := collection()
	defer coll.Database.Session.Close()
	err := coll.Find(bson.M{"_id": id}).One(&c)
	if err != nil {
		return nil, err
	}
	return &c, nil
}

func listAppContainers(appName string) ([]container, error) {
	var containers []container
	err := collection().Find(bson.M{"appname": appName}).All(&containers)
	return containers, err
}

// getImage returns the image name or id from an app.
func getImage(app provision.App) string {
	var c container
	collection().Find(bson.M{"appname": app.GetName()}).One(&c)
	if c.Image != "" {
		return c.Image
	}
	repoNamespace, err := config.GetString("docker:repository-namespace")
	if err != nil {
		return ""
	}
	return fmt.Sprintf("%s/%s", repoNamespace, app.GetPlatform())
}

// removeImage removes an image from docker registry
func removeImage(imageId string) error {
	return dockerCluster.RemoveImage(imageId)
}

type cmdError struct {
	cmd  string
	args []string
	err  error
	out  string
}

func (e *cmdError) Error() string {
	command := e.cmd + " " + strings.Join(e.args, " ")
	return fmt.Sprintf("Failed to run command %q (%s): %s.", command, e.err, e.out)
}
<|endoftext|>


<commit_before>package casper

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

	"github.com/cascades-fbp/cascades/runtime"
	. "github.com/gogap/base_component"
)

const (
	REQ_X_API   = "X-API"
	REQ_TIMEOUT = time.Duration(15) * time.Second
)

type Response struct {
	Code    uint64      `json:"code"`
	Message string      `json:"message,omitempty"`
	Result  interface{} `json:"result,omitempty"`
}

func handle(p *App) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		p.logger.Debug("http Handler:", r.Method, r.RequestURI)

		apiName := r.Header.Get(REQ_X_API)
		if apiName == "" {
			p.logger.Errorln("request api name nil")
			http.NotFound(w, r)
			return
		}
		port := p.GetApi(apiName)
		if port == nil {
			p.logger.Errorln("request api 404")
			http.NotFound(w, r)
			return
		}

		reqBody, err := ioutil.ReadAll(r.Body)
		if err != nil {
			p.logger.Errorln("request body err:", p.Name, err.Error())
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte("Read request body error"))
			return
		}
		p.logger.Debug("req:", apiName, string(reqBody))

		componentMsg, _ := NewComponentMessage()
		componentMsg.Payload.Result = reqBody

		ch := p.AddRequest(componentMsg.ID)
		defer p.DelRequest(componentMsg.ID)
		defer close(ch)

		// Send Component message
		msgBytes, err := componentMsg.Serialize()
		if err != nil {
			p.logger.Errorln("Service Internal Error")
			w.WriteHeader(http.StatusInternalServerError)
			w.Write([]byte("Service Internal Error"))
			return
		}
		p.logger.Infoln("ToNextComponent:", port.outPort[0].Url, string(msgBytes))
		port.outPort[0].Socket.SendMessage(runtime.NewPacket(msgBytes))

		// Wait for response from IN port
		p.logger.Debug("Waiting for response from a channel port (from INPUT port)")
		var load *Payload
		select {
		case load = <-ch:
			break
		case <-time.Tick(REQ_TIMEOUT):
			w.WriteHeader(http.StatusInternalServerError)
			w.Write([]byte("Couldn't process request in a given time"))
			return
		}

		p.logger.Infoln("Data arrived. Responding to HTTP response...")
		objResp := Response{
			Code:    load.Code,
			Message: load.Message,
			Result:  load.Result}

		bResp, _ := json.Marshal(objResp)
		w.Write(bResp)
	}
}
<commit_msg>update<commit_after>package casper

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

	"github.com/cascades-fbp/cascades/runtime"
	. "github.com/gogap/base_component"
)

const (
	REQ_X_API   = "X-API"
	REQ_TIMEOUT = time.Duration(15) * time.Second
)

type Response struct {
	Code    uint64      `json:"code"`
	Message string      `json:"message,omitempty"`
	Result  interface{} `json:"result,omitempty"`
}

func handle(p *App) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		p.logger.Debug("http Handler:", r.Method, r.RequestURI)

		apiName := r.Header.Get(REQ_X_API)
		if apiName == "" {
			p.logger.Errorln("request api name nil")
			http.NotFound(w, r)
			return
		}
		port := p.GetApi(apiName)
		if port == nil {
			p.logger.Errorln("request api 404")
			http.NotFound(w, r)
			return
		}

		reqBody, err := ioutil.ReadAll(r.Body)
		if err != nil {
			p.logger.Errorln("request body err:", p.Name, err.Error())
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte("Read request body error"))
			return
		}
		p.logger.Debug("req:", apiName, string(reqBody))

		componentMsg, _ := NewComponentMessage()
		componentMsg.Payload.Result = reqBody

		ch := p.AddRequest(componentMsg.ID)
		defer p.DelRequest(componentMsg.ID)
		defer close(ch)

		// Send Component message
		msgBytes, err := componentMsg.Serialize()
		if err != nil {
			p.logger.Errorln("Service Internal Error")
			w.WriteHeader(http.StatusInternalServerError)
			w.Write([]byte("Service Internal Error"))
			return
		}
		p.logger.Infoln("ToNextComponent:", port.outPort[0].Url, string(msgBytes))
		port.outPort[0].Socket.SendMessage(runtime.NewPacket(msgBytes))

		// Wait for response from IN port
		p.logger.Debug("Waiting for response from a channel port (from INPUT port)")
		var load *Payload
		select {
		case load = <-ch:
			break
		case <-time.Tick(REQ_TIMEOUT):
			w.WriteHeader(http.StatusInternalServerError)
			w.Write([]byte("Couldn't process request in a given time"))
			return
		}

		objResp := Response{
			Code:    load.Code,
			Message: load.Message,
			Result:  load.Result}

		bResp, _ := json.Marshal(objResp)
		w.Write(bResp)
		p.logger.Infoln("Data arrived. Responding to HTTP response...", string(bResp))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"sync"
	"time"

	"github.com/traetox/pty"
	"github.com/traetox/sshForShits/fakeshell"
	"golang.org/x/crypto/ssh"
)

const (
	pmpt = "sh-4.3$ "
)

func mainHandler(conn *ssh.ServerConn, chans <-chan ssh.NewChannel, reqs <-chan *ssh.Request, handler fakeshell.Handler, dg datagram) {
	wg := sync.WaitGroup{}
	wg.Add(2)
	go handleRequests(reqs, &wg)
	go handleChannels(chans, &wg, handler, dg)
	wg.Wait()
	conn.Close()
	conn.Conn.Close()
}

func handleRequests(reqs <-chan *ssh.Request, wg *sync.WaitGroup) {
	defer wg.Done()
	for _ = range reqs {
	}
}

func handleChannels(chans <-chan ssh.NewChannel, wg *sync.WaitGroup, handler fakeshell.Handler, dg datagram) {
	defer wg.Done()
	// Service the incoming Channel channel.
	for newChannel := range chans {
		// Channels have a type, depending on the application level
		// protocol intended. In the case of a shell, the type is
		// "session" and ServerShell may be used to present a simple
		// terminal interface.
		if t := newChannel.ChannelType(); t != "session" {
			newChannel.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t))
			continue
		}
		channel, requests, err := newChannel.Accept()
		if err != nil {
			errLog.Printf("Failed to Accept new channel: %v", err)
			continue
		}

		//fire up our fake shell
		c := fakeshell.New("sh-4.3$ ", handler, &dg)
		f, err := pty.StartFaker(c)
		if err != nil {
			errLog.Printf("Failed to start faker: %v", err)
			continue
		}

		//teardown session
		var once sync.Once
		close := func() {
			channel.Close()
			c.Wait()
			dg.Logout = time.Now().Format(time.RFC3339Nano)
			if len(dg.ShellActivity) > 0 {
				if err := activityClient.Write(dg); err != nil {
					errLog.Printf("Failed to write session: %v", err)
					if err := activityClient.Login(); err != nil {
						errLog.Printf("Failed to re-login after Write error: %v", err)
					}
				}
			}
		}

		//pipe session to bash and vice-versa
		go func() {
			io.Copy(channel, f)
			once.Do(close)
		}()
		go func() {
			io.Copy(f, channel)
			once.Do(close)
		}()

		// Sessions have out-of-band requests such as "shell", "pty-req" and "env"
		go func(in <-chan *ssh.Request) {
			for req := range in {
				switch req.Type {
				case "shell":
					// We don't accept any commands (Payload),
					// only the default shell.
					if len(req.Payload) == 0 {
						req.Reply(true, nil)
					} else {
						req.Reply(false, nil)
					}
				case "pty-req":
					// Responding 'ok' here will let the client
					// know we have a pty ready for input
					req.Reply(true, nil)
				case "window-change":
					continue //no response
				}
			}
		}(requests)
	}
}
<commit_msg>closing PTY device after fake shell exits<commit_after>package main

import (
	"fmt"
	"io"
	"sync"
	"time"

	"github.com/traetox/pty"
	"github.com/traetox/sshForShits/fakeshell"
	"golang.org/x/crypto/ssh"
)

const (
	pmpt = "sh-4.3$ "
)

func mainHandler(conn *ssh.ServerConn, chans <-chan ssh.NewChannel, reqs <-chan *ssh.Request, handler fakeshell.Handler, dg datagram) {
	wg := sync.WaitGroup{}
	wg.Add(2)
	go handleRequests(reqs, &wg)
	go handleChannels(chans, &wg, handler, dg)
	wg.Wait()
	conn.Close()
	conn.Conn.Close()
}

func handleRequests(reqs <-chan *ssh.Request, wg *sync.WaitGroup) {
	defer wg.Done()
	for _ = range reqs {
	}
}

func handleChannels(chans <-chan ssh.NewChannel, wg *sync.WaitGroup, handler fakeshell.Handler, dg datagram) {
	defer wg.Done()
	// Service the incoming Channel channel.
	for newChannel := range chans {
		// Channels have a type, depending on the application level
		// protocol intended. In the case of a shell, the type is
		// "session" and ServerShell may be used to present a simple
		// terminal interface.
		if t := newChannel.ChannelType(); t != "session" {
			newChannel.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t))
			continue
		}
		channel, requests, err := newChannel.Accept()
		if err != nil {
			errLog.Printf("Failed to Accept new channel: %v", err)
			continue
		}

		//fire up our fake shell
		c := fakeshell.New("sh-4.3$ ", handler, &dg)
		f, err := pty.StartFaker(c)
		if err != nil {
			errLog.Printf("Failed to start faker: %v", err)
			continue
		}

		//teardown session
		var once sync.Once
		close := func() {
			channel.Close()
			c.Wait()
			f.Close() //close the PTY device
			dg.Logout = time.Now().Format(time.RFC3339Nano)
			if len(dg.ShellActivity) > 0 {
				if err := activityClient.Write(dg); err != nil {
					errLog.Printf("Failed to write session: %v", err)
					if err := activityClient.Login(); err != nil {
						errLog.Printf("Failed to re-login after Write error: %v", err)
					}
				}
			}
		}

		//pipe session to bash and vice-versa
		go func() {
			io.Copy(channel, f)
			once.Do(close)
		}()
		go func() {
			io.Copy(f, channel)
			once.Do(close)
		}()

		// Sessions have out-of-band requests such as "shell", "pty-req" and "env"
		go func(in <-chan *ssh.Request) {
			for req := range in {
				switch req.Type {
				case "shell":
					// We don't accept any commands (Payload),
					// only the default shell.
					if len(req.Payload) == 0 {
						req.Reply(true, nil)
					} else {
						req.Reply(false, nil)
					}
				case "pty-req":
					// Responding 'ok' here will let the client
					// know we have a pty ready for input
					req.Reply(true, nil)
				case "window-change":
					continue //no response
				}
			}
		}(requests)
	}
}
<|endoftext|>


<commit_before>package fweight

import (
	"net/http"
)

//Function HandlerOf returns a Handler of a http.Handler, which
//can be used to terminatr and handle routes.
func HandlerOf(h http.Handler) Handler {
	return Handler{
		h,
	}
}

type Handler struct {
	http.Handler
}

func (h Handler) RouteHTTP(rq *http.Request) Router {
	return h
}
<commit_msg>HandlerFunc<commit_after>package fweight

import (
	"net/http"
)

//Function HandlerOf returns a Handler of a http.Handler, which
//can be used to terminatr and handle routes.
func HandlerOf(h http.Handler) Handler {
	return Handler{
		h,
	}
}

func HandlerFunc(hf http.HandlerFunc) Handler {
	return HandlerOf(hf)
}

type Handler struct {
	http.Handler
}

func (h Handler) RouteHTTP(rq *http.Request) Router {
	return h
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"strings"

	"github.com/miekg/dns"
)

func recurse(w dns.ResponseWriter, req *dns.Msg) {
	if recurseTo == "" {
		dns.HandleFailed(w, req)
		return
	} else {
		c := new(dns.Client)
	Redo:
		if in, _, err := c.Exchange(req, recurseTo); err == nil { // Second return value is RTT
			if in.MsgHdr.Truncated {
				c.Net = "tcp"
				goto Redo
			}

			w.WriteMsg(in)
			return
		} else {
			log.Printf("Recursive error: %+v\n", err)
			dns.HandleFailed(w, req)
			return
		}
	}
}

func handleDNS(w dns.ResponseWriter, req *dns.Msg) {
	// BIND does not support answering multiple questions so we won't
	var zone *Zone
	var name string

	zones.RLock()
	defer zones.RUnlock()

	if len(req.Question) != 1 {
		m := new(dns.Msg)
		m.SetReply(req)
		m.SetRcodeFormatError(req)
		w.WriteMsg(m)
		return
	}
	req.Question[0].Name = strings.ToLower(req.Question[0].Name)

	zone, name = zones.match(req.Question[0].Name, req.Question[0].Qtype)
	if zone == nil {
		if recurseTo != "" {
			recurse(w, req)
		} else {
			m := new(dns.Msg)
			m.SetReply(req)
			m.SetRcode(req, dns.RcodeRefused)
			w.WriteMsg(m)
		}
		return
	}

	zones.hits_mx.Lock()
	zones.hits[name]++
	zones.hits_mx.Unlock()

	m := new(dns.Msg)
	m.SetReply(req)
	m.Authoritative = true
	m.RecursionAvailable = false

	var (
		answerKnown bool
		rrsetExists bool
	)

	for _, r := range (*zone)[dns.RR_Header{Name: req.Question[0].Name, Class: req.Question[0].Qclass}] {
		rrsetExists = true
		if r.Header().Rrtype == req.Question[0].Qtype {
			m.Answer = append(m.Answer, r)
			answerKnown = true
		}
	}

	// check for a wildcard record (*.zone)
	if !answerKnown {
		for _, r := range (*zone)[dns.RR_Header{Name: "*." + name, Class: req.Question[0].Qclass}] {
			rrsetExists = true
			if r.Header().Rrtype == req.Question[0].Qtype {
				r.Header().Name = dns.Fqdn(req.Question[0].Name)
				m.Answer = append(m.Answer, r)
				answerKnown = true
			}
		}
	}

	if !answerKnown && rrsetExists {
		// we don't have a direct response but may find alternative record type: CNAME
		for _, r := range (*zone)[dns.RR_Header{Name: req.Question[0].Name, Rrtype: dns.TypeCNAME, Class: req.Question[0].Qclass}] {
			m.Answer = append(m.Answer, r)
			answerKnown = true
		}

		// we don't have a direct response but may find alternative record type: NS
		for _, r := range (*zone)[dns.RR_Header{Name: req.Question[0].Name, Rrtype: dns.TypeNS, Class: req.Question[0].Qclass}] {
			m.Answer = append(m.Answer, r)
			answerKnown = true
		}
	}

	// now we try recursion if enabled
	if !answerKnown && recurseTo != "" { // we don't want to handleFailed when recursion is disabled
		recurse(w, req)
		return
	}

	if !answerKnown && !rrsetExists {
		// we really don't have any record to offer
		m.SetRcode(req, dns.RcodeNameError)
		m.Ns = (*zone)[dns.RR_Header{Name: name, Rrtype: dns.TypeSOA, Class: dns.ClassINET}]
	} else if !answerKnown && rrsetExists {
		m.Ns = (*zone)[dns.RR_Header{Name: name, Rrtype: dns.TypeSOA, Class: dns.ClassINET}]
	} else {
		// Add Authority section
		for _, r := range (*zone)[dns.RR_Header{Name: name, Rrtype: dns.TypeNS, Class: dns.ClassINET}] {
			m.Ns = append(m.Ns, r)

			// Resolve Authority if possible and serve as ADDITIONAL SECTION
			zone2, _ := zones.match(r.(*dns.NS).Ns, dns.TypeA)
			if zone2 != nil {
				for _, r := range (*zone2)[dns.RR_Header{Name: r.(*dns.NS).Ns, Rrtype: dns.TypeA, Class: dns.ClassINET}] {
					m.Extra = append(m.Extra, r)
				}
				for _, r := range (*zone2)[dns.RR_Header{Name: r.(*dns.NS).Ns, Rrtype: dns.TypeAAAA, Class: dns.ClassINET}] {
					m.Extra = append(m.Extra, r)
				}
			}
		}
	}

	// Resolve extra lookups for CNAMEs, SRVs, etc. and put in ADDITIONAL SECTION
	for _, r := range m.Answer {
		switch r.Header().Rrtype {
		case dns.TypeCNAME:
			zone2, _ := zones.match(r.(*dns.CNAME).Target, dns.TypeA)
			if zone2 != nil {
				for _, r := range (*zone2)[dns.RR_Header{Name: r.(*dns.CNAME).Target, Rrtype: dns.TypeA, Class: dns.ClassINET}] {
					m.Extra = append(m.Extra, r)
				}
				for _, r := range (*zone2)[dns.RR_Header{Name: r.(*dns.CNAME).Target, Rrtype: dns.TypeAAAA, Class: dns.ClassINET}] {
					m.Extra = append(m.Extra, r)
				}
			}
		case dns.TypeSRV:
			zone2, _ := zones.match(r.(*dns.SRV).Target, dns.TypeA)
			if zone2 != nil {
				for _, r := range (*zone2)[dns.RR_Header{Name: r.(*dns.SRV).Target, Rrtype: dns.TypeA, Class: dns.ClassINET}] {
					m.Extra = append(m.Extra, r)
				}
				for _, r := range (*zone2)[dns.RR_Header{Name: r.(*dns.SRV).Target, Rrtype: dns.TypeAAAA, Class: dns.ClassINET}] {
					m.Extra = append(m.Extra, r)
				}
			}
		}
	}

	m.Answer = dns.Dedup(m.Answer, nil)
	m.Extra = dns.Dedup(m.Extra, nil)
	w.WriteMsg(m)
}

func UnFqdn(s string) string {
	if dns.IsFqdn(s) {
		return s[:len(s)-1]
	}
	return s
}
<commit_msg>Fix (not) adding authority section and NS records<commit_after>package main

import (
	"log"
	"strings"

	"github.com/miekg/dns"
)

func recurse(w dns.ResponseWriter, req *dns.Msg) {
	if recurseTo == "" {
		dns.HandleFailed(w, req)
		return
	} else {
		c := new(dns.Client)
	Redo:
		if in, _, err := c.Exchange(req, recurseTo); err == nil { // Second return value is RTT
			if in.MsgHdr.Truncated {
				c.Net = "tcp"
				goto Redo
			}

			w.WriteMsg(in)
			return
		} else {
			log.Printf("Recursive error: %+v\n", err)
			dns.HandleFailed(w, req)
			return
		}
	}
}

func handleDNS(w dns.ResponseWriter, req *dns.Msg) {
	// BIND does not support answering multiple questions so we won't
	var zone *Zone
	var name string

	zones.RLock()
	defer zones.RUnlock()

	if len(req.Question) != 1 {
		m := new(dns.Msg)
		m.SetReply(req)
		m.SetRcodeFormatError(req)
		w.WriteMsg(m)
		return
	}
	req.Question[0].Name = strings.ToLower(req.Question[0].Name)

	zone, name = zones.match(req.Question[0].Name, req.Question[0].Qtype)
	if zone == nil {
		if recurseTo != "" {
			recurse(w, req)
		} else {
			m := new(dns.Msg)
			m.SetReply(req)
			m.SetRcode(req, dns.RcodeRefused)
			w.WriteMsg(m)
		}
		return
	}

	zones.hits_mx.Lock()
	zones.hits[name]++
	zones.hits_mx.Unlock()

	m := new(dns.Msg)
	m.SetReply(req)
	m.Authoritative = true
	m.RecursionAvailable = false

	var (
		answerKnown bool
		rrsetExists bool
		noAuthority bool
	)

	for _, r := range (*zone)[dns.RR_Header{Name: req.Question[0].Name, Class: req.Question[0].Qclass}] {
		rrsetExists = true
		if r.Header().Rrtype == req.Question[0].Qtype {
			m.Answer = append(m.Answer, r)
			answerKnown = true
		}
	}

	// check for a wildcard record (*.zone)
	if !answerKnown {
		for _, r := range (*zone)[dns.RR_Header{Name: "*." + name, Class: req.Question[0].Qclass}] {
			rrsetExists = true
			if r.Header().Rrtype == req.Question[0].Qtype {
				r.Header().Name = dns.Fqdn(req.Question[0].Name)
				m.Answer = append(m.Answer, r)
				answerKnown = true
			}
		}
	}

	if !answerKnown && rrsetExists {
		// we don't have a direct response but may find alternative record type: CNAME
		for _, r := range (*zone)[dns.RR_Header{Name: req.Question[0].Name, Rrtype: dns.TypeCNAME, Class: req.Question[0].Qclass}] {
			m.Answer = append(m.Answer, r)
			answerKnown = true
			noAuthority = true
		}

		// we don't have a direct response but may find alternative record type: NS
		if (*zone)[dns.RR_Header{Name: req.Question[0].Name, Rrtype: dns.TypeNS, Class: req.Question[0].Qclass}] != nil {
			m.Ns = (*zone)[dns.RR_Header{Name: req.Question[0].Name, Rrtype: dns.TypeNS, Class: req.Question[0].Qclass}]
			answerKnown = true
			noAuthority = true
		}
	}

	// now we try recursion if enabled
	if !answerKnown && recurseTo != "" { // we don't want to handleFailed when recursion is disabled
		recurse(w, req)
		return
	}

	if !answerKnown && !rrsetExists {
		// we really don't have any record to offer
		m.SetRcode(req, dns.RcodeNameError)
		m.Ns = (*zone)[dns.RR_Header{Name: name, Rrtype: dns.TypeSOA, Class: dns.ClassINET}]
	} else if !answerKnown && rrsetExists {
		m.Ns = (*zone)[dns.RR_Header{Name: name, Rrtype: dns.TypeSOA, Class: dns.ClassINET}]
	} else if !noAuthority {
		// Add Authority section
		for _, r := range (*zone)[dns.RR_Header{Name: name, Rrtype: dns.TypeNS, Class: dns.ClassINET}] {
			m.Ns = append(m.Ns, r)

			// Resolve Authority if possible and serve as ADDITIONAL SECTION
			zone2, _ := zones.match(r.(*dns.NS).Ns, dns.TypeA)
			if zone2 != nil {
				for _, r := range (*zone2)[dns.RR_Header{Name: r.(*dns.NS).Ns, Rrtype: dns.TypeA, Class: dns.ClassINET}] {
					m.Extra = append(m.Extra, r)
				}
				for _, r := range (*zone2)[dns.RR_Header{Name: r.(*dns.NS).Ns, Rrtype: dns.TypeAAAA, Class: dns.ClassINET}] {
					m.Extra = append(m.Extra, r)
				}
			}
		}
	}

	// Resolve extra lookups for CNAMEs, SRVs, etc. and put in ADDITIONAL SECTION
	for _, r := range m.Answer {
		switch r.Header().Rrtype {
		case dns.TypeCNAME:
			zone2, _ := zones.match(r.(*dns.CNAME).Target, dns.TypeA)
			if zone2 != nil {
				for _, r := range (*zone2)[dns.RR_Header{Name: r.(*dns.CNAME).Target, Rrtype: dns.TypeA, Class: dns.ClassINET}] {
					m.Extra = append(m.Extra, r)
				}
				for _, r := range (*zone2)[dns.RR_Header{Name: r.(*dns.CNAME).Target, Rrtype: dns.TypeAAAA, Class: dns.ClassINET}] {
					m.Extra = append(m.Extra, r)
				}
			}
		case dns.TypeSRV:
			zone2, _ := zones.match(r.(*dns.SRV).Target, dns.TypeA)
			if zone2 != nil {
				for _, r := range (*zone2)[dns.RR_Header{Name: r.(*dns.SRV).Target, Rrtype: dns.TypeA, Class: dns.ClassINET}] {
					m.Extra = append(m.Extra, r)
				}
				for _, r := range (*zone2)[dns.RR_Header{Name: r.(*dns.SRV).Target, Rrtype: dns.TypeAAAA, Class: dns.ClassINET}] {
					m.Extra = append(m.Extra, r)
				}
			}
		}
	}

	m.Answer = dns.Dedup(m.Answer, nil)
	m.Extra = dns.Dedup(m.Extra, nil)
	w.WriteMsg(m)
}

func UnFqdn(s string) string {
	if dns.IsFqdn(s) {
		return s[:len(s)-1]
	}
	return s
}
<|endoftext|>


<commit_before>// Go implementation of http://www.hashids.org under MIT license
// Generates hashes from an array of integers, eg. for YouTube like hashes
// Setup: go get github.com/speps/go-hashids
// Original implementations by Ivan Akimov at https://github.com/ivanakimov
// Thanks to Rémy Oudompheng and Peter Hellberg for code review and fixes

package hashids

import (
	"errors"
	"fmt"
	"math"
	"strings"
)

const (
	// Version is the version number of the library
	Version string = "1.0.0"

	// DefaultAlphabet is the default alphabet used by go-hashids
	DefaultAlphabet string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"

	minAlphabetLength int     = 16
	sepDiv            float64 = 3.5
	guardDiv          float64 = 12.0
)

var sepsOriginal = []rune("cfhistuCFHISTU")

// HashID contains everything needed to encode/decode hashids
type HashID struct {
	alphabet           []rune
	minLength          int
	maxLengthPerNumber int
	salt               []rune
	seps               []rune
	guards             []rune
}

// HashIDData contains the information needed to generate hashids
type HashIDData struct {
	// Alphabet is the alphabet used to generate new ids
	Alphabet string

	// MinLength is the minimum length of a generated id
	MinLength int

	// Salt is the secret used to make the generated id harder to guess
	Salt string
}

// NewData creates a new HashIDData with the DefaultAlphabet already set.
func NewData() *HashIDData {
	return &HashIDData{Alphabet: DefaultAlphabet}
}

// New creates a new HashID
func New() (*HashID, error) {
	return NewWithData(NewData())
}

// NewWithData creates a new HashID with the provided HashIDData
func NewWithData(data *HashIDData) (*HashID, error) {
	if len(data.Alphabet) < minAlphabetLength {
		return nil, fmt.Errorf("alphabet must contain at least %d characters", minAlphabetLength)
	}
	if strings.Contains(data.Alphabet, " ") {
		return nil, fmt.Errorf("alphabet may not contain spaces")
	}
	// Check if all characters are unique in Alphabet
	uniqueCheck := make(map[rune]bool, len(data.Alphabet))
	for _, a := range data.Alphabet {
		if _, found := uniqueCheck[a]; found {
			return nil, fmt.Errorf("duplicate character in alphabet: %s", string([]rune{a}))
		}
		uniqueCheck[a] = true
	}

	alphabet := []rune(data.Alphabet)
	salt := []rune(data.Salt)

	seps := make([]rune, len(sepsOriginal))
	copy(seps, sepsOriginal)

	// seps should contain only characters present in alphabet; alphabet should not contains seps
	for i := 0; i < len(seps); i++ {
		foundIndex := -1
		for j, a := range alphabet {
			if a == seps[i] {
				foundIndex = j
				break
			}
		}
		if foundIndex == -1 {
			seps = append(seps[:i], seps[i+1:]...)
			i--
		} else {
			alphabet = append(alphabet[:foundIndex], alphabet[foundIndex+1:]...)
		}
	}
	seps = consistentShuffle(seps, salt)

	if len(seps) == 0 || float64(len(alphabet))/float64(len(seps)) > sepDiv {
		sepsLength := int(math.Ceil(float64(len(alphabet)) / sepDiv))
		if sepsLength == 1 {
			sepsLength++
		}
		if sepsLength > len(seps) {
			diff := sepsLength - len(seps)
			seps = append(seps, alphabet[:diff]...)
			alphabet = alphabet[diff:]
		} else {
			seps = seps[:sepsLength]
		}
	}
	alphabet = consistentShuffle(alphabet, salt)

	guardCount := int(math.Ceil(float64(len(alphabet)) / guardDiv))
	var guards []rune
	if len(alphabet) < 3 {
		guards = seps[:guardCount]
		seps = seps[guardCount:]
	} else {
		guards = alphabet[:guardCount]
		alphabet = alphabet[guardCount:]
	}

	hid := &HashID{
		alphabet:  alphabet,
		minLength: data.MinLength,
		salt:      salt,
		seps:      seps,
		guards:    guards,
	}

	// Calculate the maximum possible string length by hashing the maximum possible id
	encoded, err := hid.EncodeInt64([]int64{math.MaxInt64})
	if err != nil {
		return nil, fmt.Errorf("Unable to encode maximum int64 to find max encoded value length: %s", err)
	}
	hid.maxLengthPerNumber = len(encoded)

	return hid, nil
}

// Encode hashes an array of int to a string containing at least MinLength characters taken from the Alphabet.
// Use Decode using the same Alphabet and Salt to get back the array of int.
func (h *HashID) Encode(numbers []int) (string, error) {
	numbers64 := make([]int64, 0, len(numbers))
	for _, id := range numbers {
		numbers64 = append(numbers64, int64(id))
	}
	return h.EncodeInt64(numbers64)
}

// EncodeInt64 hashes an array of int64 to a string containing at least MinLength characters taken from the Alphabet.
// Use DecodeInt64 using the same Alphabet and Salt to get back the array of int64.
func (h *HashID) EncodeInt64(numbers []int64) (string, error) {
	if len(numbers) == 0 {
		return "", errors.New("encoding empty array of numbers makes no sense")
	}
	for _, n := range numbers {
		if n < 0 {
			return "", errors.New("negative number not supported")
		}
	}

	alphabet := make([]rune, len(h.alphabet))
	copy(alphabet, h.alphabet)

	numbersHash := int64(0)
	for i, n := range numbers {
		numbersHash += (n % int64(i+100))
	}

	maxRuneLength := h.maxLengthPerNumber * len(numbers)
	if maxRuneLength < h.minLength {
		maxRuneLength = h.minLength
	}

	result := make([]rune, 0, maxRuneLength)
	lottery := alphabet[numbersHash%int64(len(alphabet))]
	result = append(result, lottery)

	for i, n := range numbers {
		buffer := append([]rune{lottery}, append(h.salt, alphabet...)...)
		alphabet = consistentShuffle(alphabet, buffer[:len(alphabet)])
		hash := hash(n, maxRuneLength, alphabet)
		result = append(result, hash...)

		if i+1 < len(numbers) {
			n %= int64(hash[0]) + int64(i)
			result = append(result, h.seps[n%int64(len(h.seps))])
		}
	}

	if len(result) < h.minLength {
		guardIndex := (numbersHash + int64(result[0])) % int64(len(h.guards))
		result = append([]rune{h.guards[guardIndex]}, result...)

		if len(result) < h.minLength {
			guardIndex = (numbersHash + int64(result[2])) % int64(len(h.guards))
			result = append(result, h.guards[guardIndex])
		}
	}

	halfLength := len(alphabet) / 2
	for len(result) < h.minLength {
		alphabet = consistentShuffle(alphabet, alphabet)
		result = append(alphabet[halfLength:], append(result, alphabet[:halfLength]...)...)
		excess := len(result) - h.minLength
		if excess > 0 {
			result = result[excess/2 : excess/2+h.minLength]
		}
	}

	return string(result), nil
}

// DEPRECATED: Use DecodeWithError instead
// Decode unhashes the string passed to an array of int.
// It is symmetric with Encode if the Alphabet and Salt are the same ones which were used to hash.
// MinLength has no effect on Decode.
func (h *HashID) Decode(hash string) []int {
	result, err := h.DecodeWithError(hash)
	if err != nil {
		panic(err)
	}
	return result
}

// Decode unhashes the string passed to an array of int.
// It is symmetric with Encode if the Alphabet and Salt are the same ones which were used to hash.
// MinLength has no effect on Decode.
func (h *HashID) DecodeWithError(hash string) ([]int, error) {
	result64, err := h.DecodeInt64WithError(hash)
	if err != nil {
		return nil, err
	}
	result := make([]int, 0, len(result64))
	for _, id := range result64 {
		result = append(result, int(id))
	}
	return result, nil
}

// DEPRECATED: Use DecodeInt64WithError instead
// DecodeInt64 unhashes the string passed to an array of int64.
// It is symmetric with EncodeInt64 if the Alphabet and Salt are the same ones which were used to hash.
// MinLength has no effect on DecodeInt64.
func (h *HashID) DecodeInt64(hash string) []int64 {
	result, err := h.DecodeInt64WithError(hash)
	if err != nil {
		panic(err)
	}
	return result
}

// DecodeInt64 unhashes the string passed to an array of int64.
// It is symmetric with EncodeInt64 if the Alphabet and Salt are the same ones which were used to hash.
// MinLength has no effect on DecodeInt64.
func (h *HashID) DecodeInt64WithError(hash string) ([]int64, error) {
	hashes := splitRunes([]rune(hash), h.guards)
	hashIndex := 0
	if len(hashes) == 2 || len(hashes) == 3 {
		hashIndex = 1
	}

	result := make([]int64, 0)

	hashBreakdown := hashes[hashIndex]
	if len(hashBreakdown) > 0 {
		lottery := hashBreakdown[0]
		hashBreakdown = hashBreakdown[1:]
		hashes = splitRunes(hashBreakdown, h.seps)
		alphabet := []rune(h.alphabet)
		for _, subHash := range hashes {
			buffer := append([]rune{lottery}, append(h.salt, alphabet...)...)
			alphabet = consistentShuffle(alphabet, buffer[:len(alphabet)])
			number, err := unhash(subHash, alphabet)
			if err != nil {
				return nil, err
			}
			result = append(result, number)
		}
	}

	sanityCheck, _ := h.EncodeInt64(result)
	if sanityCheck != hash {
		return result, errors.New("mismatch between encode and decode")
	}

	return result, nil
}

func splitRunes(input, seps []rune) [][]rune {
	splitIndices := make([]int, 0)
	for i, inputRune := range input {
		for _, sepsRune := range seps {
			if inputRune == sepsRune {
				splitIndices = append(splitIndices, i)
			}
		}
	}

	result := make([][]rune, 0, len(splitIndices)+1)
	inputLeft := input[:]
	for _, splitIndex := range splitIndices {
		splitIndex -= len(input) - len(inputLeft)
		subInput := make([]rune, splitIndex)
		copy(subInput, inputLeft[:splitIndex])
		result = append(result, subInput)
		inputLeft = inputLeft[splitIndex+1:]
	}
	result = append(result, inputLeft)

	return result
}

func hash(input int64, maxRuneLength int, alphabet []rune) []rune {
	result := make([]rune, 0, maxRuneLength)
	for {
		r := alphabet[input%int64(len(alphabet))]
		result = append(result, r)
		input /= int64(len(alphabet))
		if input == 0 {
			break
		}
	}
	for i := len(result)/2 - 1; i >= 0; i-- {
		opp := len(result) - 1 - i
		result[i], result[opp] = result[opp], result[i]
	}
	return result
}

func unhash(input, alphabet []rune) (int64, error) {
	result := int64(0)
	for _, inputRune := range input {
		alphabetPos := -1
		for pos, alphabetRune := range alphabet {
			if inputRune == alphabetRune {
				alphabetPos = pos
				break
			}
		}
		if alphabetPos == -1 {
			return 0, errors.New("alphabet used for hash was different")
		}

		result = result*int64(len(alphabet)) + int64(alphabetPos)
	}
	return result, nil
}

func consistentShuffle(alphabet, salt []rune) []rune {
	if len(salt) == 0 {
		return alphabet
	}

	result := make([]rune, len(alphabet))
	copy(result, alphabet)
	for i, v, p := len(result)-1, 0, 0; i > 0; i-- {
		p += int(salt[v])
		j := (int(salt[v]) + v + p) % i
		result[i], result[j] = result[j], result[i]
		v = (v + 1) % len(salt)
	}

	return result
}
<commit_msg>Add a function for duplicating a slice of runes<commit_after>// Go implementation of http://www.hashids.org under MIT license
// Generates hashes from an array of integers, eg. for YouTube like hashes
// Setup: go get github.com/speps/go-hashids
// Original implementations by Ivan Akimov at https://github.com/ivanakimov
// Thanks to Rémy Oudompheng and Peter Hellberg for code review and fixes

package hashids

import (
	"errors"
	"fmt"
	"math"
	"strings"
)

const (
	// Version is the version number of the library
	Version string = "1.0.0"

	// DefaultAlphabet is the default alphabet used by go-hashids
	DefaultAlphabet string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"

	minAlphabetLength int     = 16
	sepDiv            float64 = 3.5
	guardDiv          float64 = 12.0
)

var sepsOriginal = []rune("cfhistuCFHISTU")

// HashID contains everything needed to encode/decode hashids
type HashID struct {
	alphabet           []rune
	minLength          int
	maxLengthPerNumber int
	salt               []rune
	seps               []rune
	guards             []rune
}

// HashIDData contains the information needed to generate hashids
type HashIDData struct {
	// Alphabet is the alphabet used to generate new ids
	Alphabet string

	// MinLength is the minimum length of a generated id
	MinLength int

	// Salt is the secret used to make the generated id harder to guess
	Salt string
}

// NewData creates a new HashIDData with the DefaultAlphabet already set.
func NewData() *HashIDData {
	return &HashIDData{Alphabet: DefaultAlphabet}
}

// New creates a new HashID
func New() (*HashID, error) {
	return NewWithData(NewData())
}

// NewWithData creates a new HashID with the provided HashIDData
func NewWithData(data *HashIDData) (*HashID, error) {
	if len(data.Alphabet) < minAlphabetLength {
		return nil, fmt.Errorf("alphabet must contain at least %d characters", minAlphabetLength)
	}
	if strings.Contains(data.Alphabet, " ") {
		return nil, fmt.Errorf("alphabet may not contain spaces")
	}
	// Check if all characters are unique in Alphabet
	uniqueCheck := make(map[rune]bool, len(data.Alphabet))
	for _, a := range data.Alphabet {
		if _, found := uniqueCheck[a]; found {
			return nil, fmt.Errorf("duplicate character in alphabet: %s", string([]rune{a}))
		}
		uniqueCheck[a] = true
	}

	alphabet := []rune(data.Alphabet)
	salt := []rune(data.Salt)

	seps := duplicateRuneSlice(sepsOriginal)

	// seps should contain only characters present in alphabet; alphabet should not contains seps
	for i := 0; i < len(seps); i++ {
		foundIndex := -1
		for j, a := range alphabet {
			if a == seps[i] {
				foundIndex = j
				break
			}
		}
		if foundIndex == -1 {
			seps = append(seps[:i], seps[i+1:]...)
			i--
		} else {
			alphabet = append(alphabet[:foundIndex], alphabet[foundIndex+1:]...)
		}
	}
	seps = consistentShuffle(seps, salt)

	if len(seps) == 0 || float64(len(alphabet))/float64(len(seps)) > sepDiv {
		sepsLength := int(math.Ceil(float64(len(alphabet)) / sepDiv))
		if sepsLength == 1 {
			sepsLength++
		}
		if sepsLength > len(seps) {
			diff := sepsLength - len(seps)
			seps = append(seps, alphabet[:diff]...)
			alphabet = alphabet[diff:]
		} else {
			seps = seps[:sepsLength]
		}
	}
	alphabet = consistentShuffle(alphabet, salt)

	guardCount := int(math.Ceil(float64(len(alphabet)) / guardDiv))
	var guards []rune
	if len(alphabet) < 3 {
		guards = seps[:guardCount]
		seps = seps[guardCount:]
	} else {
		guards = alphabet[:guardCount]
		alphabet = alphabet[guardCount:]
	}

	hid := &HashID{
		alphabet:  alphabet,
		minLength: data.MinLength,
		salt:      salt,
		seps:      seps,
		guards:    guards,
	}

	// Calculate the maximum possible string length by hashing the maximum possible id
	encoded, err := hid.EncodeInt64([]int64{math.MaxInt64})
	if err != nil {
		return nil, fmt.Errorf("Unable to encode maximum int64 to find max encoded value length: %s", err)
	}
	hid.maxLengthPerNumber = len(encoded)

	return hid, nil
}

// Encode hashes an array of int to a string containing at least MinLength characters taken from the Alphabet.
// Use Decode using the same Alphabet and Salt to get back the array of int.
func (h *HashID) Encode(numbers []int) (string, error) {
	numbers64 := make([]int64, 0, len(numbers))
	for _, id := range numbers {
		numbers64 = append(numbers64, int64(id))
	}
	return h.EncodeInt64(numbers64)
}

// EncodeInt64 hashes an array of int64 to a string containing at least MinLength characters taken from the Alphabet.
// Use DecodeInt64 using the same Alphabet and Salt to get back the array of int64.
func (h *HashID) EncodeInt64(numbers []int64) (string, error) {
	if len(numbers) == 0 {
		return "", errors.New("encoding empty array of numbers makes no sense")
	}
	for _, n := range numbers {
		if n < 0 {
			return "", errors.New("negative number not supported")
		}
	}

	alphabet := duplicateRuneSlice(h.alphabet)

	numbersHash := int64(0)
	for i, n := range numbers {
		numbersHash += (n % int64(i+100))
	}

	maxRuneLength := h.maxLengthPerNumber * len(numbers)
	if maxRuneLength < h.minLength {
		maxRuneLength = h.minLength
	}

	result := make([]rune, 0, maxRuneLength)
	lottery := alphabet[numbersHash%int64(len(alphabet))]
	result = append(result, lottery)

	for i, n := range numbers {
		buffer := append([]rune{lottery}, append(h.salt, alphabet...)...)
		alphabet = consistentShuffle(alphabet, buffer[:len(alphabet)])
		hash := hash(n, maxRuneLength, alphabet)
		result = append(result, hash...)

		if i+1 < len(numbers) {
			n %= int64(hash[0]) + int64(i)
			result = append(result, h.seps[n%int64(len(h.seps))])
		}
	}

	if len(result) < h.minLength {
		guardIndex := (numbersHash + int64(result[0])) % int64(len(h.guards))
		result = append([]rune{h.guards[guardIndex]}, result...)

		if len(result) < h.minLength {
			guardIndex = (numbersHash + int64(result[2])) % int64(len(h.guards))
			result = append(result, h.guards[guardIndex])
		}
	}

	halfLength := len(alphabet) / 2
	for len(result) < h.minLength {
		alphabet = consistentShuffle(alphabet, alphabet)
		result = append(alphabet[halfLength:], append(result, alphabet[:halfLength]...)...)
		excess := len(result) - h.minLength
		if excess > 0 {
			result = result[excess/2 : excess/2+h.minLength]
		}
	}

	return string(result), nil
}

// DEPRECATED: Use DecodeWithError instead
// Decode unhashes the string passed to an array of int.
// It is symmetric with Encode if the Alphabet and Salt are the same ones which were used to hash.
// MinLength has no effect on Decode.
func (h *HashID) Decode(hash string) []int {
	result, err := h.DecodeWithError(hash)
	if err != nil {
		panic(err)
	}
	return result
}

// Decode unhashes the string passed to an array of int.
// It is symmetric with Encode if the Alphabet and Salt are the same ones which were used to hash.
// MinLength has no effect on Decode.
func (h *HashID) DecodeWithError(hash string) ([]int, error) {
	result64, err := h.DecodeInt64WithError(hash)
	if err != nil {
		return nil, err
	}
	result := make([]int, 0, len(result64))
	for _, id := range result64 {
		result = append(result, int(id))
	}
	return result, nil
}

// DEPRECATED: Use DecodeInt64WithError instead
// DecodeInt64 unhashes the string passed to an array of int64.
// It is symmetric with EncodeInt64 if the Alphabet and Salt are the same ones which were used to hash.
// MinLength has no effect on DecodeInt64.
func (h *HashID) DecodeInt64(hash string) []int64 {
	result, err := h.DecodeInt64WithError(hash)
	if err != nil {
		panic(err)
	}
	return result
}

// DecodeInt64 unhashes the string passed to an array of int64.
// It is symmetric with EncodeInt64 if the Alphabet and Salt are the same ones which were used to hash.
// MinLength has no effect on DecodeInt64.
func (h *HashID) DecodeInt64WithError(hash string) ([]int64, error) {
	hashes := splitRunes([]rune(hash), h.guards)
	hashIndex := 0
	if len(hashes) == 2 || len(hashes) == 3 {
		hashIndex = 1
	}

	result := make([]int64, 0)

	hashBreakdown := hashes[hashIndex]
	if len(hashBreakdown) > 0 {
		lottery := hashBreakdown[0]
		hashBreakdown = hashBreakdown[1:]
		hashes = splitRunes(hashBreakdown, h.seps)
		alphabet := []rune(h.alphabet)
		for _, subHash := range hashes {
			buffer := append([]rune{lottery}, append(h.salt, alphabet...)...)
			alphabet = consistentShuffle(alphabet, buffer[:len(alphabet)])
			number, err := unhash(subHash, alphabet)
			if err != nil {
				return nil, err
			}
			result = append(result, number)
		}
	}

	sanityCheck, _ := h.EncodeInt64(result)
	if sanityCheck != hash {
		return result, errors.New("mismatch between encode and decode")
	}

	return result, nil
}

func splitRunes(input, seps []rune) [][]rune {
	splitIndices := make([]int, 0)
	for i, inputRune := range input {
		for _, sepsRune := range seps {
			if inputRune == sepsRune {
				splitIndices = append(splitIndices, i)
			}
		}
	}

	result := make([][]rune, 0, len(splitIndices)+1)
	inputLeft := input[:]
	for _, splitIndex := range splitIndices {
		splitIndex -= len(input) - len(inputLeft)
		subInput := make([]rune, splitIndex)
		copy(subInput, inputLeft[:splitIndex])
		result = append(result, subInput)
		inputLeft = inputLeft[splitIndex+1:]
	}
	result = append(result, inputLeft)

	return result
}

func hash(input int64, maxRuneLength int, alphabet []rune) []rune {
	result := make([]rune, 0, maxRuneLength)
	for {
		r := alphabet[input%int64(len(alphabet))]
		result = append(result, r)
		input /= int64(len(alphabet))
		if input == 0 {
			break
		}
	}
	for i := len(result)/2 - 1; i >= 0; i-- {
		opp := len(result) - 1 - i
		result[i], result[opp] = result[opp], result[i]
	}
	return result
}

func unhash(input, alphabet []rune) (int64, error) {
	result := int64(0)
	for _, inputRune := range input {
		alphabetPos := -1
		for pos, alphabetRune := range alphabet {
			if inputRune == alphabetRune {
				alphabetPos = pos
				break
			}
		}
		if alphabetPos == -1 {
			return 0, errors.New("alphabet used for hash was different")
		}

		result = result*int64(len(alphabet)) + int64(alphabetPos)
	}
	return result, nil
}

func consistentShuffle(alphabet, salt []rune) []rune {
	if len(salt) == 0 {
		return alphabet
	}

	result := duplicateRuneSlice(alphabet)
	for i, v, p := len(result)-1, 0, 0; i > 0; i-- {
		p += int(salt[v])
		j := (int(salt[v]) + v + p) % i
		result[i], result[j] = result[j], result[i]
		v = (v + 1) % len(salt)
	}

	return result
}

func duplicateRuneSlice(data []rune) []rune {
	result := make([]rune, len(data))
	copy(result, data)
	return result
}
<|endoftext|>