<commit_before>package syslog

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"fmt"
	"io"
	"net"
	"net/url"
	"os"
	"strconv"
	"strings"
	"text/template"
	"time"

	"github.com/segmentio/ecs-logs/lib"
)

const (
	DefaultTemplate = "<{{.PRIVAL}}>{{.TIMESTAMP}} {{.GROUP}}[{{.STREAM}}]: {{.MSG}}"
)

type WriterConfig struct {
	Network    string
	Address    string
	Template   string
	TimeFormat string
	Tag        string
	TLS        *tls.Config
}

func NewWriter(group string, stream string) (w ecslogs.Writer, err error) {
	var c WriterConfig
	var s string
	var u *url.URL

	if s = os.Getenv("SYSLOG_URL"); len(s) != 0 {
		if u, err = url.Parse(s); err != nil {
			err = fmt.Errorf("invalid syslog URL: %s", err)
			return
		}

		c.Network = u.Scheme
		c.Address = u.Host
	}

	c.Template = os.Getenv("SYSLOG_TEMPLATE")
	c.TimeFormat = os.Getenv("SYSLOG_TIME_FORMAT")

	return DialWriter(c)
}

func DialWriter(config WriterConfig) (w ecslogs.Writer, err error) {
	var netopts []string
	var addropts []string
	var backend io.Writer

	if len(config.Network) != 0 {
		netopts = []string{config.Network}
	} else if len(config.Address) == 0 || strings.HasPrefix(config.Address, "/") {
		// When starting with a '/' we assume it's gonna be a file path,
		// otherwise we fallback to trying a TLS connection so we don't
		// implicitly send logs over an unsecured link.
		config.Network = "unix"
		netopts = []string{"unixgram", "unix"}
	} else {
		netopts = []string{"tls"}
	}

	if len(config.Address) != 0 {
		addropts = []string{config.Address}
	} else if strings.HasPrefix(config.Network, "unix") {
		// This was copied from the standard log/syslog package, they do the same
		// and try to guess at runtime which socket syslogd is using.
		addropts = []string{"/dev/log", "/var/run/syslog", "/var/run/log"}
	} else {
		// The config doesn't point to a unix domain socket, falling back to trying
		// to connect to syslogd over a network interface.
		addropts = []string{"localhost:514"}
	}

connect:
	for _, n := range netopts {
		for _, a := range addropts {
			if backend, err = dialWriter(n, a, config.TLS); err == nil {
				break connect
			}
		}
	}

	if err != nil {
		return
	}

	w = newWriter(writerConfig{
		backend:    backend,
		template:   config.Template,
		timeFormat: config.TimeFormat,
	})
	return
}

type writerConfig struct {
	backend    io.Writer
	template   string
	timeFormat string
	tag        string
}

func newWriter(config writerConfig) *writer {
	var out func(*writer, message) error
	var flush func() error

	if len(config.timeFormat) == 0 {
		config.timeFormat = time.Stamp
	}

	if len(config.template) == 0 {
		config.template = DefaultTemplate
	}

	switch b := config.backend.(type) {
	case bufferedWriter:
		out, flush = (*writer).directWrite, b.Flush
	default:
		out, flush = (*writer).bufferedWrite, func() error { return nil }
	}

	return &writer{
		writerConfig: config,
		flush:        flush,
		out:          out,
		tpl:          newWriterTemplate(config.template),
	}
}

func newWriterTemplate(format string) *template.Template {
	if !strings.HasSuffix(format, "\n") {
		format += "\n"
	}
	t := template.New("syslog")
	template.Must(t.Parse(format))
	return t
}

type writer struct {
	writerConfig
	buf   bytes.Buffer
	tpl   *template.Template
	out   func(*writer, message) error
	flush func() error
}

func (w *writer) Close() (err error) {
	if c, ok := w.backend.(io.Closer); ok {
		err = c.Close()
	}
	return
}

func (w *writer) WriteMessageBatch(batch []ecslogs.Message) (err error) {
	for _, msg := range batch {
		if err = w.write(msg); err != nil {
			return
		}
	}
	return w.flush()
}

func (w *writer) WriteMessage(msg ecslogs.Message) (err error) {
	if err = w.write(msg); err == nil {
		err = w.flush()
	}
	return
}

func (w *writer) write(msg ecslogs.Message) (err error) {
	m := message{
		PRIVAL:    int(msg.Level) + 8, // +8 is for user-level messages facility
		HOSTNAME:  msg.Host,
		MSGID:     msg.ID,
		GROUP:     msg.Group,
		STREAM:    msg.Stream,
		MSG:       msg.Content,
		TIMESTAMP: msg.Time.Format(w.timeFormat),
		TAG:       w.tag,
	}

	if len(m.HOSTNAME) == 0 {
		m.HOSTNAME = "-"
	}

	if len(m.MSGID) == 0 {
		m.MSGID = "-"
	}

	if msg.PID == 0 {
		m.PROCID = "-"
	} else {
		m.PROCID = strconv.Itoa(msg.PID)
	}

	if len(msg.File) != 0 || len(msg.Func) != 0 {
		m.SOURCE = fmt.Sprintf("%s:%s:%d", msg.File, msg.Func, msg.Line)
	}

	return w.out(w, m)
}

func (w *writer) directWrite(m message) (err error) {
	return w.tpl.Execute(w.backend, m)
}

func (w *writer) bufferedWrite(m message) (err error) {
	w.buf.Reset()
	w.tpl.Execute(&w.buf, m)
	_, err = w.backend.Write(w.buf.Bytes())
	return
}

type message struct {
	PRIVAL    int
	HOSTNAME  string
	PROCID    string
	MSGID     string
	GROUP     string
	STREAM    string
	TAG       string
	MSG       string
	SOURCE    string
	TIMESTAMP string
}

type bufferedWriter interface {
	Flush() error
}

type bufferedConn struct {
	buf  *bufio.Writer
	conn net.Conn
}

func (c bufferedConn) Close() error                { return c.conn.Close() }
func (c bufferedConn) Flush() error                { return c.buf.Flush() }
func (c bufferedConn) Write(b []byte) (int, error) { return c.buf.Write(b) }

func dialWriter(network string, address string, config *tls.Config) (w io.Writer, err error) {
	var conn net.Conn

	if network == "tls" {
		conn, err = tls.Dial("tcp", address, config)
	} else {
		conn, err = net.Dial(network, address)
	}

	if err == nil {
		switch network {
		case "udp", "udp4", "udp6", "unixgram", "unixpacket":
			w = conn
		default:
			w = bufferedConn{
				conn: conn,
				buf:  bufio.NewWriter(conn),
			}
		}
	}

	return
}
<commit_msg>set tag<commit_after>package syslog

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"fmt"
	"io"
	"net"
	"net/url"
	"os"
	"strconv"
	"strings"
	"text/template"
	"time"

	"github.com/segmentio/ecs-logs/lib"
)

const (
	DefaultTemplate = "<{{.PRIVAL}}>{{.TIMESTAMP}} {{.GROUP}}[{{.STREAM}}]: {{.MSG}}"
)

type WriterConfig struct {
	Network    string
	Address    string
	Template   string
	TimeFormat string
	Tag        string
	TLS        *tls.Config
}

func NewWriter(group string, stream string) (w ecslogs.Writer, err error) {
	var c WriterConfig
	var s string
	var u *url.URL

	if s = os.Getenv("SYSLOG_URL"); len(s) != 0 {
		if u, err = url.Parse(s); err != nil {
			err = fmt.Errorf("invalid syslog URL: %s", err)
			return
		}

		c.Network = u.Scheme
		c.Address = u.Host
	}

	c.Template = os.Getenv("SYSLOG_TEMPLATE")
	c.TimeFormat = os.Getenv("SYSLOG_TIME_FORMAT")

	return DialWriter(c)
}

func DialWriter(config WriterConfig) (w ecslogs.Writer, err error) {
	var netopts []string
	var addropts []string
	var backend io.Writer

	if len(config.Network) != 0 {
		netopts = []string{config.Network}
	} else if len(config.Address) == 0 || strings.HasPrefix(config.Address, "/") {
		// When starting with a '/' we assume it's gonna be a file path,
		// otherwise we fallback to trying a TLS connection so we don't
		// implicitly send logs over an unsecured link.
		config.Network = "unix"
		netopts = []string{"unixgram", "unix"}
	} else {
		netopts = []string{"tls"}
	}

	if len(config.Address) != 0 {
		addropts = []string{config.Address}
	} else if strings.HasPrefix(config.Network, "unix") {
		// This was copied from the standard log/syslog package, they do the same
		// and try to guess at runtime which socket syslogd is using.
		addropts = []string{"/dev/log", "/var/run/syslog", "/var/run/log"}
	} else {
		// The config doesn't point to a unix domain socket, falling back to trying
		// to connect to syslogd over a network interface.
		addropts = []string{"localhost:514"}
	}

connect:
	for _, n := range netopts {
		for _, a := range addropts {
			if backend, err = dialWriter(n, a, config.TLS); err == nil {
				break connect
			}
		}
	}

	if err != nil {
		return
	}

	w = newWriter(writerConfig{
		backend:    backend,
		template:   config.Template,
		timeFormat: config.TimeFormat,
		tag:        config.Tag,
	})
	return
}

type writerConfig struct {
	backend    io.Writer
	template   string
	timeFormat string
	tag        string
}

func newWriter(config writerConfig) *writer {
	var out func(*writer, message) error
	var flush func() error

	if len(config.timeFormat) == 0 {
		config.timeFormat = time.Stamp
	}

	if len(config.template) == 0 {
		config.template = DefaultTemplate
	}

	switch b := config.backend.(type) {
	case bufferedWriter:
		out, flush = (*writer).directWrite, b.Flush
	default:
		out, flush = (*writer).bufferedWrite, func() error { return nil }
	}

	return &writer{
		writerConfig: config,
		flush:        flush,
		out:          out,
		tpl:          newWriterTemplate(config.template),
	}
}

func newWriterTemplate(format string) *template.Template {
	if !strings.HasSuffix(format, "\n") {
		format += "\n"
	}
	t := template.New("syslog")
	template.Must(t.Parse(format))
	return t
}

type writer struct {
	writerConfig
	buf   bytes.Buffer
	tpl   *template.Template
	out   func(*writer, message) error
	flush func() error
}

func (w *writer) Close() (err error) {
	if c, ok := w.backend.(io.Closer); ok {
		err = c.Close()
	}
	return
}

func (w *writer) WriteMessageBatch(batch []ecslogs.Message) (err error) {
	for _, msg := range batch {
		if err = w.write(msg); err != nil {
			return
		}
	}
	return w.flush()
}

func (w *writer) WriteMessage(msg ecslogs.Message) (err error) {
	if err = w.write(msg); err == nil {
		err = w.flush()
	}
	return
}

func (w *writer) write(msg ecslogs.Message) (err error) {
	m := message{
		PRIVAL:    int(msg.Level) + 8, // +8 is for user-level messages facility
		HOSTNAME:  msg.Host,
		MSGID:     msg.ID,
		GROUP:     msg.Group,
		STREAM:    msg.Stream,
		MSG:       msg.Content,
		TIMESTAMP: msg.Time.Format(w.timeFormat),
		TAG:       w.tag,
	}

	if len(m.HOSTNAME) == 0 {
		m.HOSTNAME = "-"
	}

	if len(m.MSGID) == 0 {
		m.MSGID = "-"
	}

	if msg.PID == 0 {
		m.PROCID = "-"
	} else {
		m.PROCID = strconv.Itoa(msg.PID)
	}

	if len(msg.File) != 0 || len(msg.Func) != 0 {
		m.SOURCE = fmt.Sprintf("%s:%s:%d", msg.File, msg.Func, msg.Line)
	}

	return w.out(w, m)
}

func (w *writer) directWrite(m message) (err error) {
	return w.tpl.Execute(w.backend, m)
}

func (w *writer) bufferedWrite(m message) (err error) {
	w.buf.Reset()
	w.tpl.Execute(&w.buf, m)
	_, err = w.backend.Write(w.buf.Bytes())
	return
}

type message struct {
	PRIVAL    int
	HOSTNAME  string
	PROCID    string
	MSGID     string
	GROUP     string
	STREAM    string
	TAG       string
	MSG       string
	SOURCE    string
	TIMESTAMP string
}

type bufferedWriter interface {
	Flush() error
}

type bufferedConn struct {
	buf  *bufio.Writer
	conn net.Conn
}

func (c bufferedConn) Close() error                { return c.conn.Close() }
func (c bufferedConn) Flush() error                { return c.buf.Flush() }
func (c bufferedConn) Write(b []byte) (int, error) { return c.buf.Write(b) }

func dialWriter(network string, address string, config *tls.Config) (w io.Writer, err error) {
	var conn net.Conn

	if network == "tls" {
		conn, err = tls.Dial("tcp", address, config)
	} else {
		conn, err = net.Dial(network, address)
	}

	if err == nil {
		switch network {
		case "udp", "udp4", "udp6", "unixgram", "unixpacket":
			w = conn
		default:
			w = bufferedConn{
				conn: conn,
				buf:  bufio.NewWriter(conn),
			}
		}
	}

	return
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/Comcast/webpa-common/logging"
	"io/ioutil"
	"net/http"
	"time"
)

type Send func(inFunc func(workerID int)) error

// Below is the struct that will implement our ServeHTTP method
type ServerHandler struct {
	logging.Logger
	caduceusHandler RequestHandler
	caduceusHealth  HealthTracker
	doJob           Send
}

func (sh *ServerHandler) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	defer request.Body.Close()

	sh.Info("Receiving incoming request...")

	timeStamps := CaduceusTimestamps{
		TimeReceived: time.Now(),
	}

	if request.Method != "POST" {
		response.WriteHeader(http.StatusBadRequest)
		response.Write([]byte(fmt.Sprintf("Unsupported method \"%s\"... Caduceus only supports \"POST\" method.\n", request.Method)))
		return
	}

	myPayload, err := ioutil.ReadAll(request.Body)
	if err != nil {
		response.WriteHeader(http.StatusBadRequest)
		response.Write([]byte(fmt.Sprintf("Unable to retrieve the request body: %s.\n", err.Error)))
		return
	}

	var contentType string
	if value, ok := request.Header["Content-Type"]; ok {
		if len(value) == 1 {
			contentType = value[0]
			switch contentType {
			case "application/json":
				// ok contentType
			case "application/wrp":
				// ok contentType
			default:
				response.WriteHeader(http.StatusBadRequest)
				response.Write([]byte("Only Content-Type values of \"application/json\" or \"application/wrp\" are supported.\n"))
				return
			}
		} else {
			response.WriteHeader(http.StatusBadRequest)
			response.Write([]byte("Content-Type cannot have more than one specification.\n"))
		}
	} else {
		response.WriteHeader(http.StatusBadRequest)
		response.Write([]byte("Content-Type must be set in the header.\n"))
	}

	if contentType == "" {
		return
	}

	targetURL := request.URL.String()

	caduceusRequest := CaduceusRequest{
		Payload:     myPayload,
		ContentType: contentType,
		TargetURL:   targetURL,
		Timestamps:  timeStamps,
	}

	caduceusRequest.Timestamps.TimeAccepted = time.Now()

	err = sh.doJob(func(workerID int) { sh.caduceusHandler.HandleRequest(workerID, caduceusRequest) })
	if err != nil {
		// return a 408
		response.WriteHeader(http.StatusRequestTimeout)
		response.Write([]byte("Unable to handle request at this time.\n"))
	} else {
		// return a 202
		response.WriteHeader(http.StatusAccepted)
		response.Write([]byte("Request placed on to queue.\n"))
		sh.caduceusHealth.IncrementBucket(len(myPayload))
	}
}

type ProfileHandler struct {
	profilerData ServerProfiler
	logging.Logger
}

// ServeHTTP method of ProfileHandler will output the most recent messages
// that the main handler has successfully dealt with
func (ph *ProfileHandler) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	defer request.Body.Close()

	ph.Info("Receiving request for server stats...")
	stats := ph.profilerData.Report()
	b, err := json.Marshal(stats)

	if err != nil {
		response.WriteHeader(http.StatusInternalServerError)
		response.Write([]byte("Error marshalling the data into a JSON object."))
	} else {
		response.WriteHeader(http.StatusOK)
		response.Write(b)
		response.Write([]byte("\n"))
	}
}
<commit_msg>Fix the content-type to be json when json and ensure that an empty document is valid json.<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/Comcast/webpa-common/logging"
	"io/ioutil"
	"net/http"
	"time"
)

type Send func(inFunc func(workerID int)) error

// Below is the struct that will implement our ServeHTTP method
type ServerHandler struct {
	logging.Logger
	caduceusHandler RequestHandler
	caduceusHealth  HealthTracker
	doJob           Send
}

func (sh *ServerHandler) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	defer request.Body.Close()

	sh.Info("Receiving incoming request...")

	timeStamps := CaduceusTimestamps{
		TimeReceived: time.Now(),
	}

	if request.Method != "POST" {
		response.WriteHeader(http.StatusBadRequest)
		response.Write([]byte(fmt.Sprintf("Unsupported method \"%s\"... Caduceus only supports \"POST\" method.\n", request.Method)))
		return
	}

	myPayload, err := ioutil.ReadAll(request.Body)
	if err != nil {
		response.WriteHeader(http.StatusBadRequest)
		response.Write([]byte(fmt.Sprintf("Unable to retrieve the request body: %s.\n", err.Error)))
		return
	}

	var contentType string
	if value, ok := request.Header["Content-Type"]; ok {
		if len(value) == 1 {
			contentType = value[0]
			switch contentType {
			case "application/json":
				// ok contentType
			case "application/wrp":
				// ok contentType
			default:
				response.WriteHeader(http.StatusBadRequest)
				response.Write([]byte("Only Content-Type values of \"application/json\" or \"application/wrp\" are supported.\n"))
				return
			}
		} else {
			response.WriteHeader(http.StatusBadRequest)
			response.Write([]byte("Content-Type cannot have more than one specification.\n"))
		}
	} else {
		response.WriteHeader(http.StatusBadRequest)
		response.Write([]byte("Content-Type must be set in the header.\n"))
	}

	if contentType == "" {
		return
	}

	targetURL := request.URL.String()

	caduceusRequest := CaduceusRequest{
		Payload:     myPayload,
		ContentType: contentType,
		TargetURL:   targetURL,
		Timestamps:  timeStamps,
	}

	caduceusRequest.Timestamps.TimeAccepted = time.Now()

	err = sh.doJob(func(workerID int) { sh.caduceusHandler.HandleRequest(workerID, caduceusRequest) })
	if err != nil {
		// return a 408
		response.WriteHeader(http.StatusRequestTimeout)
		response.Write([]byte("Unable to handle request at this time.\n"))
	} else {
		// return a 202
		response.WriteHeader(http.StatusAccepted)
		response.Write([]byte("Request placed on to queue.\n"))
		sh.caduceusHealth.IncrementBucket(len(myPayload))
	}
}

type ProfileHandler struct {
	profilerData ServerProfiler
	logging.Logger
}

// ServeHTTP method of ProfileHandler will output the most recent messages
// that the main handler has successfully dealt with
func (ph *ProfileHandler) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	defer request.Body.Close()

	ph.Info("Receiving request for server stats...")
	stats := ph.profilerData.Report()
	b, err := json.Marshal(stats)

	if nil == stats {
		b = []byte("[]")
		err = nil
	}

	if err != nil {
		response.WriteHeader(http.StatusInternalServerError)
		response.Write([]byte("Error marshalling the data into a JSON object."))
	} else {
		response.Header().Set("Content-Type", "application/json")
		response.WriteHeader(http.StatusOK)
		response.Write(b)
		response.Write([]byte("\n"))
	}
}
<|endoftext|>


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

package controllers

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"

	"github.com/goharbor/harbor/src/common"
	"github.com/goharbor/harbor/src/common/dao"
	"github.com/goharbor/harbor/src/common/models"
	"github.com/goharbor/harbor/src/common/utils"
	"github.com/goharbor/harbor/src/common/utils/log"
	"github.com/goharbor/harbor/src/common/utils/oidc"
	"github.com/goharbor/harbor/src/core/api"
	"github.com/goharbor/harbor/src/core/config"
	"github.com/pkg/errors"
)

const tokenKey = "oidc_token"
const stateKey = "oidc_state"
const userInfoKey = "oidc_user_info"
const oidcUserComment = "Onboarded via OIDC provider"

// OIDCController handles requests for OIDC login, callback and user onboard
type OIDCController struct {
	api.BaseController
}

type onboardReq struct {
	Username string `json:"username"`
}

type oidcUserData struct {
	Issuer   string `json:"iss"`
	Subject  string `json:"sub"`
	Username string `json:"name"`
	Email    string `json:"email"`
}

// Prepare include public code path for call request handler of OIDCController
func (oc *OIDCController) Prepare() {
	if mode, _ := config.AuthMode(); mode != common.OIDCAuth {
		oc.SendPreconditionFailedError(fmt.Errorf("Auth Mode: %s is not OIDC based", mode))
		return
	}
}

// RedirectLogin redirect user's browser to OIDC provider's login page
func (oc *OIDCController) RedirectLogin() {
	state := utils.GenerateRandomString()
	url, err := oidc.AuthCodeURL(state)
	if err != nil {
		oc.SendInternalServerError(err)
		return
	}
	oc.SetSession(stateKey, state)
	log.Debugf("State dumped to session: %s", state)
	// Force to use the func 'Redirect' of beego.Controller
	oc.Controller.Redirect(url, http.StatusFound)
}

// Callback handles redirection from OIDC provider.  It will exchange the token and
// kick off onboard if needed.
func (oc *OIDCController) Callback() {
	if oc.Ctx.Request.URL.Query().Get("state") != oc.GetSession(stateKey) {
		log.Errorf("State mismatch, in session: %s, in url: %s", oc.GetSession(stateKey),
			oc.Ctx.Request.URL.Query().Get("state"))
		oc.SendBadRequestError(errors.New("State mismatch"))
		return
	}
	code := oc.Ctx.Request.URL.Query().Get("code")
	ctx := oc.Ctx.Request.Context()
	token, err := oidc.ExchangeToken(ctx, code)
	if err != nil {
		log.Errorf("Failed to exchange token, error: %v", err)
		// Return a 4xx error so user can see the details in case it's due to misconfiguration.
		oc.SendBadRequestError(err)
		return
	}
	log.Debugf("ID token from provider: %s", token.IDToken)

	idToken, err := oidc.VerifyToken(ctx, token.IDToken)
	if err != nil {
		oc.SendInternalServerError(err)
		return
	}
	d := &oidcUserData{}
	err = idToken.Claims(d)
	if err != nil {
		oc.SendInternalServerError(err)
		return
	}
	ouDataStr, err := json.Marshal(d)
	if err != nil {
		oc.SendInternalServerError(err)
		return
	}
	u, err := dao.GetUserBySubIss(d.Subject, d.Issuer)
	if err != nil {
		oc.SendInternalServerError(err)
		return
	}

	tokenBytes, err := json.Marshal(token)
	if err != nil {
		oc.SendInternalServerError(err)
		return
	}
	oc.SetSession(tokenKey, tokenBytes)

	if u == nil {
		oc.SetSession(userInfoKey, string(ouDataStr))
		oc.Controller.Redirect(fmt.Sprintf("/oidc-onboard?username=%s", strings.Replace(d.Username, " ", "_", -1)),
			http.StatusFound)
	} else {
		oidcUser, err := dao.GetOIDCUserByUserID(u.UserID)
		if err != nil {
			oc.SendInternalServerError(err)
			return
		}
		_, t, err := secretAndToken(tokenBytes)
		oidcUser.Token = t
		if err := dao.UpdateOIDCUser(oidcUser); err != nil {
			oc.SendInternalServerError(err)
			return
		}
		oc.SetSession(userKey, *u)
		oc.Controller.Redirect("/", http.StatusFound)
	}
}

// Onboard handles the request to onboard a user authenticated via OIDC provider
func (oc *OIDCController) Onboard() {
	u := &onboardReq{}
	if err := oc.DecodeJSONReq(u); err != nil {
		oc.SendBadRequestError(err)
		return
	}
	username := u.Username
	if utils.IsIllegalLength(username, 1, 255) {
		oc.SendBadRequestError(errors.New("username with illegal length"))
		return
	}
	if utils.IsContainIllegalChar(username, []string{",", "~", "#", "$", "%"}) {
		oc.SendBadRequestError(errors.New("username contains illegal characters"))
		return
	}

	userInfoStr, ok := oc.GetSession(userInfoKey).(string)
	if !ok {
		oc.SendBadRequestError(errors.New("Failed to get OIDC user info from session"))
		return
	}
	log.Debugf("User info string: %s\n", userInfoStr)
	tb, ok := oc.GetSession(tokenKey).([]byte)
	if !ok {
		oc.SendBadRequestError(errors.New("Failed to get OIDC token from session"))
		return
	}
	s, t, err := secretAndToken(tb)
	if err != nil {
		oc.SendInternalServerError(err)
		return
	}
	d := &oidcUserData{}
	err = json.Unmarshal([]byte(userInfoStr), &d)
	if err != nil {
		oc.SendInternalServerError(err)
		return
	}
	oidcUser := models.OIDCUser{
		SubIss: d.Subject + d.Issuer,
		Secret: s,
		Token:  t,
	}

	email := d.Email
	user := models.User{
		Username:     username,
		Realname:     d.Username,
		Email:        email,
		OIDCUserMeta: &oidcUser,
		Comment:      oidcUserComment,
	}

	err = dao.OnBoardOIDCUser(&user)
	if err != nil {
		if strings.Contains(err.Error(), dao.ErrDupUser.Error()) {
			oc.RenderError(http.StatusConflict, "Conflict in username, the user with same username has been onboarded.")
			return
		}
		oc.SendInternalServerError(err)
		oc.DelSession(userInfoKey)
		return
	}

	user.OIDCUserMeta = nil
	oc.SetSession(userKey, user)
	oc.DelSession(userInfoKey)
}

func secretAndToken(tokenBytes []byte) (string, string, error) {
	key, err := config.SecretKey()
	if err != nil {
		return "", "", err
	}
	token, err := utils.ReversibleEncrypt((string)(tokenBytes), key)
	if err != nil {
		return "", "", err
	}
	str := utils.GenerateRandomString()
	secret, err := utils.ReversibleEncrypt(str, key)
	if err != nil {
		return "", "", err
	}
	return secret, token, nil
}
<commit_msg>Handle error on OIDC callback<commit_after>// Copyright 2018 Project Harbor Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package controllers

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"

	"github.com/goharbor/harbor/src/common"
	"github.com/goharbor/harbor/src/common/dao"
	"github.com/goharbor/harbor/src/common/models"
	"github.com/goharbor/harbor/src/common/utils"
	"github.com/goharbor/harbor/src/common/utils/log"
	"github.com/goharbor/harbor/src/common/utils/oidc"
	"github.com/goharbor/harbor/src/core/api"
	"github.com/goharbor/harbor/src/core/config"
	"github.com/pkg/errors"
)

const tokenKey = "oidc_token"
const stateKey = "oidc_state"
const userInfoKey = "oidc_user_info"
const oidcUserComment = "Onboarded via OIDC provider"

// OIDCController handles requests for OIDC login, callback and user onboard
type OIDCController struct {
	api.BaseController
}

type onboardReq struct {
	Username string `json:"username"`
}

type oidcUserData struct {
	Issuer   string `json:"iss"`
	Subject  string `json:"sub"`
	Username string `json:"name"`
	Email    string `json:"email"`
}

// Prepare include public code path for call request handler of OIDCController
func (oc *OIDCController) Prepare() {
	if mode, _ := config.AuthMode(); mode != common.OIDCAuth {
		oc.SendPreconditionFailedError(fmt.Errorf("Auth Mode: %s is not OIDC based", mode))
		return
	}
}

// RedirectLogin redirect user's browser to OIDC provider's login page
func (oc *OIDCController) RedirectLogin() {
	state := utils.GenerateRandomString()
	url, err := oidc.AuthCodeURL(state)
	if err != nil {
		oc.SendInternalServerError(err)
		return
	}
	oc.SetSession(stateKey, state)
	log.Debugf("State dumped to session: %s", state)
	// Force to use the func 'Redirect' of beego.Controller
	oc.Controller.Redirect(url, http.StatusFound)
}

// Callback handles redirection from OIDC provider.  It will exchange the token and
// kick off onboard if needed.
func (oc *OIDCController) Callback() {
	if oc.Ctx.Request.URL.Query().Get("state") != oc.GetSession(stateKey) {
		log.Errorf("State mismatch, in session: %s, in url: %s", oc.GetSession(stateKey),
			oc.Ctx.Request.URL.Query().Get("state"))
		oc.SendBadRequestError(errors.New("State mismatch"))
		return
	}

	errorCode := oc.Ctx.Request.URL.Query().Get("error")
	if errorCode != "" {
		errorDescription := oc.Ctx.Request.URL.Query().Get("error_description")
		log.Errorf("OIDC callback returned error: %s - %s", errorCode, errorDescription)
		oc.SendBadRequestError(errors.Errorf("OIDC callback returned error: %s - %s", errorCode, errorDescription))
		return
	}

	code := oc.Ctx.Request.URL.Query().Get("code")
	ctx := oc.Ctx.Request.Context()
	token, err := oidc.ExchangeToken(ctx, code)
	if err != nil {
		log.Errorf("Failed to exchange token, error: %v", err)
		// Return a 4xx error so user can see the details in case it's due to misconfiguration.
		oc.SendBadRequestError(err)
		return
	}
	log.Debugf("ID token from provider: %s", token.IDToken)

	idToken, err := oidc.VerifyToken(ctx, token.IDToken)
	if err != nil {
		oc.SendInternalServerError(err)
		return
	}
	d := &oidcUserData{}
	err = idToken.Claims(d)
	if err != nil {
		oc.SendInternalServerError(err)
		return
	}
	ouDataStr, err := json.Marshal(d)
	if err != nil {
		oc.SendInternalServerError(err)
		return
	}
	u, err := dao.GetUserBySubIss(d.Subject, d.Issuer)
	if err != nil {
		oc.SendInternalServerError(err)
		return
	}

	tokenBytes, err := json.Marshal(token)
	if err != nil {
		oc.SendInternalServerError(err)
		return
	}
	oc.SetSession(tokenKey, tokenBytes)

	if u == nil {
		oc.SetSession(userInfoKey, string(ouDataStr))
		oc.Controller.Redirect(fmt.Sprintf("/oidc-onboard?username=%s", strings.Replace(d.Username, " ", "_", -1)),
			http.StatusFound)
	} else {
		oidcUser, err := dao.GetOIDCUserByUserID(u.UserID)
		if err != nil {
			oc.SendInternalServerError(err)
			return
		}
		_, t, err := secretAndToken(tokenBytes)
		oidcUser.Token = t
		if err := dao.UpdateOIDCUser(oidcUser); err != nil {
			oc.SendInternalServerError(err)
			return
		}
		oc.SetSession(userKey, *u)
		oc.Controller.Redirect("/", http.StatusFound)
	}
}

// Onboard handles the request to onboard a user authenticated via OIDC provider
func (oc *OIDCController) Onboard() {
	u := &onboardReq{}
	if err := oc.DecodeJSONReq(u); err != nil {
		oc.SendBadRequestError(err)
		return
	}
	username := u.Username
	if utils.IsIllegalLength(username, 1, 255) {
		oc.SendBadRequestError(errors.New("username with illegal length"))
		return
	}
	if utils.IsContainIllegalChar(username, []string{",", "~", "#", "$", "%"}) {
		oc.SendBadRequestError(errors.New("username contains illegal characters"))
		return
	}

	userInfoStr, ok := oc.GetSession(userInfoKey).(string)
	if !ok {
		oc.SendBadRequestError(errors.New("Failed to get OIDC user info from session"))
		return
	}
	log.Debugf("User info string: %s\n", userInfoStr)
	tb, ok := oc.GetSession(tokenKey).([]byte)
	if !ok {
		oc.SendBadRequestError(errors.New("Failed to get OIDC token from session"))
		return
	}
	s, t, err := secretAndToken(tb)
	if err != nil {
		oc.SendInternalServerError(err)
		return
	}
	d := &oidcUserData{}
	err = json.Unmarshal([]byte(userInfoStr), &d)
	if err != nil {
		oc.SendInternalServerError(err)
		return
	}
	oidcUser := models.OIDCUser{
		SubIss: d.Subject + d.Issuer,
		Secret: s,
		Token:  t,
	}

	email := d.Email
	user := models.User{
		Username:     username,
		Realname:     d.Username,
		Email:        email,
		OIDCUserMeta: &oidcUser,
		Comment:      oidcUserComment,
	}

	err = dao.OnBoardOIDCUser(&user)
	if err != nil {
		if strings.Contains(err.Error(), dao.ErrDupUser.Error()) {
			oc.RenderError(http.StatusConflict, "Conflict in username, the user with same username has been onboarded.")
			return
		}
		oc.SendInternalServerError(err)
		oc.DelSession(userInfoKey)
		return
	}

	user.OIDCUserMeta = nil
	oc.SetSession(userKey, user)
	oc.DelSession(userInfoKey)
}

func secretAndToken(tokenBytes []byte) (string, string, error) {
	key, err := config.SecretKey()
	if err != nil {
		return "", "", err
	}
	token, err := utils.ReversibleEncrypt((string)(tokenBytes), key)
	if err != nil {
		return "", "", err
	}
	str := utils.GenerateRandomString()
	secret, err := utils.ReversibleEncrypt(str, key)
	if err != nil {
		return "", "", err
	}
	return secret, token, nil
}
<|endoftext|>


<commit_before>// Copyright 2010 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.

package math

// The original C code, the long comment, and the constants
// below are from FreeBSD's /usr/src/lib/msun/src/s_log1p.c
// and came with this notice.  The go code is a simplified
// version of the original C.
//
// ====================================================
// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
//
// Developed at SunPro, a Sun Microsystems, Inc. business.
// Permission to use, copy, modify, and distribute this
// software is freely granted, provided that this notice
// is preserved.
// ====================================================
//
//
// double log1p(double x)
//
// Method :
//   1. Argument Reduction: find k and f such that
//                      1+x = 2**k * (1+f),
//         where  sqrt(2)/2 < 1+f < sqrt(2) .
//
//      Note. If k=0, then f=x is exact. However, if k!=0, then f
//      may not be representable exactly. In that case, a correction
//      term is need. Let u=1+x rounded. Let c = (1+x)-u, then
//      log(1+x) - log(u) ~ c/u. Thus, we proceed to compute log(u),
//      and add back the correction term c/u.
//      (Note: when x > 2**53, one can simply return log(x))
//
//   2. Approximation of log1p(f).
//      Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s)
//               = 2s + 2/3 s**3 + 2/5 s**5 + .....,
//               = 2s + s*R
//      We use a special Reme algorithm on [0,0.1716] to generate
//      a polynomial of degree 14 to approximate R The maximum error
//      of this polynomial approximation is bounded by 2**-58.45. In
//      other words,
//                      2      4      6      8      10      12      14
//          R(z) ~ Lp1*s +Lp2*s +Lp3*s +Lp4*s +Lp5*s  +Lp6*s  +Lp7*s
//      (the values of Lp1 to Lp7 are listed in the program)
//      a-0.2929nd
//          |      2          14          |     -58.45
//          | Lp1*s +...+Lp7*s    -  R(z) | <= 2
//          |                             |
//      Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2.
//      In order to guarantee error in log below 1ulp, we compute log
//      by
//              log1p(f) = f - (hfsq - s*(hfsq+R)).
//
//   3. Finally, log1p(x) = k*ln2 + log1p(f).
//                        = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo)))
//      Here ln2 is split into two floating point number:
//                   ln2_hi + ln2_lo,
//      where n*ln2_hi is always exact for |n| < 2000.
//
// Special cases:
//      log1p(x) is NaN with signal if x < -1 (including -INF) ;
//      log1p(+INF) is +INF; log1p(-1) is -INF with signal;
//      log1p(NaN) is that NaN with no signal.
//
// Accuracy:
//      according to an error analysis, the error is always less than
//      1 ulp (unit in the last place).
//
// Constants:
// The hexadecimal values are the intended ones for the following
// constants. The decimal values may be used, provided that the
// compiler will convert from decimal to binary accurately enough
// to produce the hexadecimal values shown.
//
// Note: Assuming log() return accurate answer, the following
//       algorithm can be used to compute log1p(x) to within a few ULP:
//
//              u = 1+x;
//              if(u==1.0) return x ; else
//                         return log(u)*(x/(u-1.0));
//
//       See HP-15C Advanced Functions Handbook, p.193.

// Log1p returns the natural logarithm of 1 plus its argument x.
// It is more accurate than Log(1 + x) when x is near zero.
//
// Special cases are:
//	Log1p(+Inf) = +Inf
//	Log1p(-1) = -Inf
//	Log1p(x < -1) = NaN
//	Log1p(NaN) = NaN
func Log1p(x float64) float64 {
	const (
		Sqrt2M1     = 4.142135623730950488017e-01  // Sqrt(2)-1 = 0x3fda827999fcef34
		Sqrt2HalfM1 = -2.928932188134524755992e-01 // Sqrt(2)/2-1 = 0xbfd2bec333018866
		Small       = 1.0 / (1 << 29)              // 2**-29 = 0x3e20000000000000
		Tiny        = 1.0 / (1 << 54)              // 2**-54
		Two53       = 1 << 53                      // 2**53
		Ln2Hi       = 6.93147180369123816490e-01   // 3fe62e42fee00000
		Ln2Lo       = 1.90821492927058770002e-10   // 3dea39ef35793c76
		Lp1         = 6.666666666666735130e-01     // 3FE5555555555593
		Lp2         = 3.999999999940941908e-01     // 3FD999999997FA04
		Lp3         = 2.857142874366239149e-01     // 3FD2492494229359
		Lp4         = 2.222219843214978396e-01     // 3FCC71C51D8E78AF
		Lp5         = 1.818357216161805012e-01     // 3FC7466496CB03DE
		Lp6         = 1.531383769920937332e-01     // 3FC39A09D078C69F
		Lp7         = 1.479819860511658591e-01     // 3FC2F112DF3E5244
	)

	// special cases
	// TODO(rsc): Remove manual inlining of IsNaN, IsInf
	// when compiler does it for us
	switch {
	case x < -1 || x != x: // x < -1 || IsNaN(x): // includes -Inf
		return NaN()
	case x == -1:
		return Inf(-1)
	case x > MaxFloat64: // IsInf(x, 1):
		return Inf(1)
	}

	absx := x
	if absx < 0 {
		absx = -absx
	}

	var f float64
	var iu uint64
	k := 1
	if absx < Sqrt2M1 { //  |x| < Sqrt(2)-1
		if absx < Small { // |x| < 2**-29
			if absx < Tiny { // |x| < 2**-54
				return x
			}
			return x - x*x*0.5
		}
		if x > Sqrt2HalfM1 { // Sqrt(2)/2-1 < x
			// (Sqrt(2)/2-1) < x < (Sqrt(2)-1)
			k = 0
			f = x
			iu = 1
		}
	}
	var c float64
	if k != 0 {
		var u float64
		if absx < Two53 { // 1<<53
			u = 1.0 + x
			iu = Float64bits(u)
			k = int((iu >> 52) - 1023)
			if k > 0 {
				c = 1.0 - (u - x)
			} else {
				c = x - (u - 1.0) // correction term
				c /= u
			}
		} else {
			u = x
			iu = Float64bits(u)
			k = int((iu >> 52) - 1023)
			c = 0
		}
		iu &= 0x000fffffffffffff
		if iu < 0x0006a09e667f3bcd { // mantissa of Sqrt(2)
			u = Float64frombits(iu | 0x3ff0000000000000) // normalize u
		} else {
			k += 1
			u = Float64frombits(iu | 0x3fe0000000000000) // normalize u/2
			iu = (0x0010000000000000 - iu) >> 2
		}
		f = u - 1.0 // Sqrt(2)/2 < u < Sqrt(2)
	}
	hfsq := 0.5 * f * f
	var s, R, z float64
	if iu == 0 { // |f| < 2**-20
		if f == 0 {
			if k == 0 {
				return 0
			} else {
				c += float64(k) * Ln2Lo
				return float64(k)*Ln2Hi + c
			}
		}
		R = hfsq * (1.0 - 0.66666666666666666*f) // avoid division
		if k == 0 {
			return f - R
		}
		return float64(k)*Ln2Hi - ((R - (float64(k)*Ln2Lo + c)) - f)
	}
	s = f / (2.0 + f)
	z = s * s
	R = z * (Lp1 + z*(Lp2+z*(Lp3+z*(Lp4+z*(Lp5+z*(Lp6+z*Lp7))))))
	if k == 0 {
		return f - (hfsq - s*(hfsq+R))
	}
	return float64(k)*Ln2Hi - ((hfsq - (s*(hfsq+R) + (float64(k)*Ln2Lo + c))) - f)
}
<commit_msg>math: fix typo in Log1p comments<commit_after>// Copyright 2010 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.

package math

// The original C code, the long comment, and the constants
// below are from FreeBSD's /usr/src/lib/msun/src/s_log1p.c
// and came with this notice.  The go code is a simplified
// version of the original C.
//
// ====================================================
// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
//
// Developed at SunPro, a Sun Microsystems, Inc. business.
// Permission to use, copy, modify, and distribute this
// software is freely granted, provided that this notice
// is preserved.
// ====================================================
//
//
// double log1p(double x)
//
// Method :
//   1. Argument Reduction: find k and f such that
//                      1+x = 2**k * (1+f),
//         where  sqrt(2)/2 < 1+f < sqrt(2) .
//
//      Note. If k=0, then f=x is exact. However, if k!=0, then f
//      may not be representable exactly. In that case, a correction
//      term is need. Let u=1+x rounded. Let c = (1+x)-u, then
//      log(1+x) - log(u) ~ c/u. Thus, we proceed to compute log(u),
//      and add back the correction term c/u.
//      (Note: when x > 2**53, one can simply return log(x))
//
//   2. Approximation of log1p(f).
//      Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s)
//               = 2s + 2/3 s**3 + 2/5 s**5 + .....,
//               = 2s + s*R
//      We use a special Reme algorithm on [0,0.1716] to generate
//      a polynomial of degree 14 to approximate R The maximum error
//      of this polynomial approximation is bounded by 2**-58.45. In
//      other words,
//                      2      4      6      8      10      12      14
//          R(z) ~ Lp1*s +Lp2*s +Lp3*s +Lp4*s +Lp5*s  +Lp6*s  +Lp7*s
//      (the values of Lp1 to Lp7 are listed in the program)
//      and
//          |      2          14          |     -58.45
//          | Lp1*s +...+Lp7*s    -  R(z) | <= 2
//          |                             |
//      Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2.
//      In order to guarantee error in log below 1ulp, we compute log
//      by
//              log1p(f) = f - (hfsq - s*(hfsq+R)).
//
//   3. Finally, log1p(x) = k*ln2 + log1p(f).
//                        = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo)))
//      Here ln2 is split into two floating point number:
//                   ln2_hi + ln2_lo,
//      where n*ln2_hi is always exact for |n| < 2000.
//
// Special cases:
//      log1p(x) is NaN with signal if x < -1 (including -INF) ;
//      log1p(+INF) is +INF; log1p(-1) is -INF with signal;
//      log1p(NaN) is that NaN with no signal.
//
// Accuracy:
//      according to an error analysis, the error is always less than
//      1 ulp (unit in the last place).
//
// Constants:
// The hexadecimal values are the intended ones for the following
// constants. The decimal values may be used, provided that the
// compiler will convert from decimal to binary accurately enough
// to produce the hexadecimal values shown.
//
// Note: Assuming log() return accurate answer, the following
//       algorithm can be used to compute log1p(x) to within a few ULP:
//
//              u = 1+x;
//              if(u==1.0) return x ; else
//                         return log(u)*(x/(u-1.0));
//
//       See HP-15C Advanced Functions Handbook, p.193.

// Log1p returns the natural logarithm of 1 plus its argument x.
// It is more accurate than Log(1 + x) when x is near zero.
//
// Special cases are:
//	Log1p(+Inf) = +Inf
//	Log1p(±0) = ±0
//	Log1p(-1) = -Inf
//	Log1p(x < -1) = NaN
//	Log1p(NaN) = NaN
func Log1p(x float64) float64 {
	const (
		Sqrt2M1     = 4.142135623730950488017e-01  // Sqrt(2)-1 = 0x3fda827999fcef34
		Sqrt2HalfM1 = -2.928932188134524755992e-01 // Sqrt(2)/2-1 = 0xbfd2bec333018866
		Small       = 1.0 / (1 << 29)              // 2**-29 = 0x3e20000000000000
		Tiny        = 1.0 / (1 << 54)              // 2**-54
		Two53       = 1 << 53                      // 2**53
		Ln2Hi       = 6.93147180369123816490e-01   // 3fe62e42fee00000
		Ln2Lo       = 1.90821492927058770002e-10   // 3dea39ef35793c76
		Lp1         = 6.666666666666735130e-01     // 3FE5555555555593
		Lp2         = 3.999999999940941908e-01     // 3FD999999997FA04
		Lp3         = 2.857142874366239149e-01     // 3FD2492494229359
		Lp4         = 2.222219843214978396e-01     // 3FCC71C51D8E78AF
		Lp5         = 1.818357216161805012e-01     // 3FC7466496CB03DE
		Lp6         = 1.531383769920937332e-01     // 3FC39A09D078C69F
		Lp7         = 1.479819860511658591e-01     // 3FC2F112DF3E5244
	)

	// special cases
	// TODO(rsc): Remove manual inlining of IsNaN, IsInf
	// when compiler does it for us
	switch {
	case x < -1 || x != x: // x < -1 || IsNaN(x): // includes -Inf
		return NaN()
	case x == -1:
		return Inf(-1)
	case x > MaxFloat64: // IsInf(x, 1):
		return Inf(1)
	}

	absx := x
	if absx < 0 {
		absx = -absx
	}

	var f float64
	var iu uint64
	k := 1
	if absx < Sqrt2M1 { //  |x| < Sqrt(2)-1
		if absx < Small { // |x| < 2**-29
			if absx < Tiny { // |x| < 2**-54
				return x
			}
			return x - x*x*0.5
		}
		if x > Sqrt2HalfM1 { // Sqrt(2)/2-1 < x
			// (Sqrt(2)/2-1) < x < (Sqrt(2)-1)
			k = 0
			f = x
			iu = 1
		}
	}
	var c float64
	if k != 0 {
		var u float64
		if absx < Two53 { // 1<<53
			u = 1.0 + x
			iu = Float64bits(u)
			k = int((iu >> 52) - 1023)
			if k > 0 {
				c = 1.0 - (u - x)
			} else {
				c = x - (u - 1.0) // correction term
				c /= u
			}
		} else {
			u = x
			iu = Float64bits(u)
			k = int((iu >> 52) - 1023)
			c = 0
		}
		iu &= 0x000fffffffffffff
		if iu < 0x0006a09e667f3bcd { // mantissa of Sqrt(2)
			u = Float64frombits(iu | 0x3ff0000000000000) // normalize u
		} else {
			k += 1
			u = Float64frombits(iu | 0x3fe0000000000000) // normalize u/2
			iu = (0x0010000000000000 - iu) >> 2
		}
		f = u - 1.0 // Sqrt(2)/2 < u < Sqrt(2)
	}
	hfsq := 0.5 * f * f
	var s, R, z float64
	if iu == 0 { // |f| < 2**-20
		if f == 0 {
			if k == 0 {
				return 0
			} else {
				c += float64(k) * Ln2Lo
				return float64(k)*Ln2Hi + c
			}
		}
		R = hfsq * (1.0 - 0.66666666666666666*f) // avoid division
		if k == 0 {
			return f - R
		}
		return float64(k)*Ln2Hi - ((R - (float64(k)*Ln2Lo + c)) - f)
	}
	s = f / (2.0 + f)
	z = s * s
	R = z * (Lp1 + z*(Lp2+z*(Lp3+z*(Lp4+z*(Lp5+z*(Lp6+z*Lp7))))))
	if k == 0 {
		return f - (hfsq - s*(hfsq+R))
	}
	return float64(k)*Ln2Hi - ((hfsq - (s*(hfsq+R) + (float64(k)*Ln2Lo + c))) - f)
}
<|endoftext|>


<commit_before>package terraform

import (
	"log"

	"github.com/hashicorp/terraform/configs"
	"github.com/hashicorp/terraform/dag"
)

// ModuleExpansionTransformer is a GraphTransformer that adds graph nodes
// representing the possible expansion of each module call in the configuration,
// and ensures that any nodes representing objects declared within a module
// are dependent on the expansion node so that they will be visited only
// after the module expansion has been decided.
//
// This transform must be applied only after all nodes representing objects
// that can be contained within modules have already been added.
type ModuleExpansionTransformer struct {
	Config *configs.Config

	// Concrete allows injection of a wrapped module node by the graph builder
	// to alter the evaluation behavior.
	Concrete ConcreteModuleNodeFunc

	closers map[string]*nodeCloseModule
}

func (t *ModuleExpansionTransformer) Transform(g *Graph) error {
	t.closers = make(map[string]*nodeCloseModule)
	// The root module is always a singleton and so does not need expansion
	// processing, but any descendent modules do. We'll process them
	// recursively using t.transform.
	for _, cfg := range t.Config.Children {
		err := t.transform(g, cfg, nil)
		if err != nil {
			return err
		}
	}

	// Now go through and connect all nodes to their respective module closers.
	// This is done all at once here, because orphaned modules were already
	// handled by the RemovedModuleTransformer, and those module closers are in
	// the graph already, and need to be connected to their parent closers.
	for _, v := range g.Vertices() {
		// skip closers so they don't attach to themselves
		if _, ok := v.(graphNodeModuleCloser); ok {
			continue
		}
		// any node that executes within the scope of a module should be a
		// GraphNodeModulePath
		pather, ok := v.(GraphNodeModulePath)
		if !ok {
			continue
		}
		if closer, ok := t.closers[pather.ModulePath().String()]; ok {
			// The module closer depends on each child resource instance, since
			// during apply the module expansion will complete before the
			// individual instances are applied.
			g.Connect(dag.BasicEdge(closer, v))
		}
	}

	return nil
}

func (t *ModuleExpansionTransformer) transform(g *Graph, c *configs.Config, parentNode dag.Vertex) error {
	_, call := c.Path.Call()
	modCall := c.Parent.Module.ModuleCalls[call.Name]

	n := &nodeExpandModule{
		Addr:       c.Path,
		Config:     c.Module,
		ModuleCall: modCall,
	}
	var v dag.Vertex = n
	if t.Concrete != nil {
		v = t.Concrete(n)
	}

	g.Add(v)
	log.Printf("[TRACE] ModuleExpansionTransformer: Added %s as %T", c.Path, v)

	if parentNode != nil {
		log.Printf("[TRACE] ModuleExpansionTransformer: %s must wait for expansion of %s", dag.VertexName(v), dag.VertexName(parentNode))
		g.Connect(dag.BasicEdge(v, parentNode))
	}

	// Add the closer (which acts as the root module node) to provide a
	// single exit point for the expanded module.
	closer := &nodeCloseModule{
		Addr: c.Path,
	}
	g.Add(closer)
	g.Connect(dag.BasicEdge(closer, v))
	t.closers[c.Path.String()] = closer

	for _, childV := range g.Vertices() {
		pather, ok := childV.(GraphNodeModulePath)
		if !ok {
			continue
		}
		if pather.ModulePath().Equal(c.Path) {
			log.Printf("[TRACE] ModuleExpansionTransformer: %s must wait for expansion of %s", dag.VertexName(childV), c.Path)
			g.Connect(dag.BasicEdge(childV, v))
		}
	}

	// Also visit child modules, recursively.
	for _, cc := range c.Children {
		return t.transform(g, cc, v)
	}

	return nil
}
<commit_msg>restore the ordering of nested modules<commit_after>package terraform

import (
	"log"

	"github.com/hashicorp/terraform/configs"
	"github.com/hashicorp/terraform/dag"
)

// ModuleExpansionTransformer is a GraphTransformer that adds graph nodes
// representing the possible expansion of each module call in the configuration,
// and ensures that any nodes representing objects declared within a module
// are dependent on the expansion node so that they will be visited only
// after the module expansion has been decided.
//
// This transform must be applied only after all nodes representing objects
// that can be contained within modules have already been added.
type ModuleExpansionTransformer struct {
	Config *configs.Config

	// Concrete allows injection of a wrapped module node by the graph builder
	// to alter the evaluation behavior.
	Concrete ConcreteModuleNodeFunc

	closers map[string]*nodeCloseModule
}

func (t *ModuleExpansionTransformer) Transform(g *Graph) error {
	t.closers = make(map[string]*nodeCloseModule)
	// The root module is always a singleton and so does not need expansion
	// processing, but any descendent modules do. We'll process them
	// recursively using t.transform.
	for _, cfg := range t.Config.Children {
		err := t.transform(g, cfg, nil)
		if err != nil {
			return err
		}
	}

	// Now go through and connect all nodes to their respective module closers.
	// This is done all at once here, because orphaned modules were already
	// handled by the RemovedModuleTransformer, and those module closers are in
	// the graph already, and need to be connected to their parent closers.
	for _, v := range g.Vertices() {
		// skip closers so they don't attach to themselves
		if _, ok := v.(graphNodeModuleCloser); ok {
			continue
		}
		// any node that executes within the scope of a module should be a
		// GraphNodeModulePath
		pather, ok := v.(GraphNodeModulePath)
		if !ok {
			continue
		}
		if closer, ok := t.closers[pather.ModulePath().String()]; ok {
			// The module closer depends on each child resource instance, since
			// during apply the module expansion will complete before the
			// individual instances are applied.
			g.Connect(dag.BasicEdge(closer, v))
		}
	}

	// Modules implicitly depend on their child modules, so connect closers to
	// other which contain their path.
	for _, c := range t.closers {
		for _, d := range t.closers {
			if len(d.Addr) > len(c.Addr) && c.Addr.Equal(d.Addr[:len(c.Addr)]) {
				g.Connect(dag.BasicEdge(c, d))
			}
		}
	}

	return nil
}

func (t *ModuleExpansionTransformer) transform(g *Graph, c *configs.Config, parentNode dag.Vertex) error {
	_, call := c.Path.Call()
	modCall := c.Parent.Module.ModuleCalls[call.Name]

	n := &nodeExpandModule{
		Addr:       c.Path,
		Config:     c.Module,
		ModuleCall: modCall,
	}
	var v dag.Vertex = n
	if t.Concrete != nil {
		v = t.Concrete(n)
	}

	g.Add(v)
	log.Printf("[TRACE] ModuleExpansionTransformer: Added %s as %T", c.Path, v)

	if parentNode != nil {
		log.Printf("[TRACE] ModuleExpansionTransformer: %s must wait for expansion of %s", dag.VertexName(v), dag.VertexName(parentNode))
		g.Connect(dag.BasicEdge(v, parentNode))
	}

	// Add the closer (which acts as the root module node) to provide a
	// single exit point for the expanded module.
	closer := &nodeCloseModule{
		Addr: c.Path,
	}
	g.Add(closer)
	g.Connect(dag.BasicEdge(closer, v))
	t.closers[c.Path.String()] = closer

	for _, childV := range g.Vertices() {
		pather, ok := childV.(GraphNodeModulePath)
		if !ok {
			continue
		}
		if pather.ModulePath().Equal(c.Path) {
			log.Printf("[TRACE] ModuleExpansionTransformer: %s must wait for expansion of %s", dag.VertexName(childV), c.Path)
			g.Connect(dag.BasicEdge(childV, v))
		}
	}

	// Also visit child modules, recursively.
	for _, cc := range c.Children {
		return t.transform(g, cc, v)
	}

	return nil
}
<|endoftext|>


<commit_before>package msg

// Bundle is a groups of messages.
type Bundle struct {
	filter   Filter
	index    map[string]int
	messages []Message
}

type Filter uint

const (
	AllMessages Filter = iota
	TranslatedOnly
	UntranslatedOnly
)

// NewBundle creates a new Bundle of Messages.
func NewBundle() *Bundle {
	return &Bundle{index: make(map[string]int, 0), messages: make([]Message, 0)}
}

// AddMessage adds a new Message to the Bundle.
// If the new Message has a non-empty translation, then it will overwrite any
// existing Message in the Bundle that has the same id.
func (b *Bundle) AddMessage(m Message) {
	if b.filter == TranslatedOnly && m.Translation == "" {
		return
	}
	m.Id = Id(m.Context, m.Content)
	if i, found := b.index[m.Id]; found && m.Translation != "" {
		b.messages[i] = m
	} else {
		b.messages = append(b.messages, m)
	}
}

// Messages returns the messages that match the filter.
func (b *Bundle) Messages(filter Filter) []Message {
	if filter == AllMessages {
		return b.messages
	}

	msgs := make([]Message, 0, len(b.messages))
	for _, m := range b.messages {
		if filter == UntranslatedOnly && m.Translation == "" ||
			filter == TranslatedOnly && m.Translation != "" {
			msgs = append(msgs, m)
		}
	}
	return msgs
}

// Len implementes sort.Interface
func (b *Bundle) Len() int {
	return len(b.messages)
}

// Less implementes sort.Interface
func (b *Bundle) Less(i, j int) bool {
	mi := b.messages[i]
	mj := b.messages[j]
	if mi.Content < mj.Content {
		return true
	}
	if mi.Content > mj.Content {
		return false
	}
	return mi.Context < mj.Context
}

// Swap implementes sort.Interface
func (b *Bundle) Swap(i, j int) {
	b.messages[i], b.messages[j] = b.messages[j], b.messages[i]
}
<commit_msg>AddMessages<commit_after>package msg

// Bundle is a groups of messages.
type Bundle struct {
	filter   Filter
	index    map[string]int
	messages []Message
}

type Filter uint

const (
	AllMessages Filter = iota
	TranslatedOnly
	UntranslatedOnly
)

// NewBundle creates a new Bundle of Messages.
func NewBundle() *Bundle {
	return &Bundle{index: make(map[string]int, 0), messages: make([]Message, 0)}
}

// AddMessage adds a message to the Bundle.
// If the new message has a non-empty translation, then it will
// replace any message in the Bundle that has the same id.
func (b *Bundle) AddMessage(m Message) {
	if b.filter == TranslatedOnly && m.Translation == "" {
		return
	}
	m.Id = Id(m.Context, m.Content)
	if i, found := b.index[m.Id]; found && m.Translation != "" {
		b.messages[i] = m
	} else {
		b.messages = append(b.messages, m)
	}
}

// AddMessages adds one or more messages to the Bundle.
// New messages that have a non-empty translation will replace
// messages in the Bundle that have the same id.
func (b *Bundle) AddMessages(msgs []Message) {
	for i, _ := range msgs {
		b.AddMessage(msgs[i])
	}
}

// Messages returns the messages that match the filter.
func (b *Bundle) Messages(filter Filter) []Message {
	if filter == AllMessages {
		return b.messages
	}

	msgs := make([]Message, 0, len(b.messages))
	for _, m := range b.messages {
		if filter == UntranslatedOnly && m.Translation == "" ||
			filter == TranslatedOnly && m.Translation != "" {
			msgs = append(msgs, m)
		}
	}
	return msgs
}

// Len implementes sort.Interface
func (b *Bundle) Len() int {
	return len(b.messages)
}

// Less implementes sort.Interface
func (b *Bundle) Less(i, j int) bool {
	mi := b.messages[i]
	mj := b.messages[j]
	if mi.Content < mj.Content {
		return true
	}
	if mi.Content > mj.Content {
		return false
	}
	return mi.Context < mj.Context
}

// Swap implementes sort.Interface
func (b *Bundle) Swap(i, j int) {
	b.messages[i], b.messages[j] = b.messages[j], b.messages[i]
}
<|endoftext|>


<commit_before>// Copyright 2009 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.

package time

import (
	"os"
	"syscall"
)

// Sleep pauses the current goroutine for ns nanoseconds.
// It returns os.EINTR if interrupted.
func Sleep(ns int64) os.Error { return os.NewSyscallError("sleep", syscall.Sleep(ns)) }
<commit_msg>Update documentation around time.Sleep to specify its precision, and suggest a possible interface for more granular sleep times.<commit_after>// Copyright 2009 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.

package time

import (
	"os"
	"syscall"
)

// Sleep pauses the current goroutine for at least ns nanoseconds. Higher resolution
// sleeping may be provided by syscall.Nanosleep on some operating systems.
// Sleep returns os.EINTR if interrupted.
func Sleep(ns int64) os.Error { return os.NewSyscallError("sleep", syscall.Sleep(ns)) }
<|endoftext|>


<commit_before>package aws

import (
	"encoding/json"
	"fmt"
	"log"
	"regexp"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/lambda"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

var LambdaFunctionRegexp = `^(arn:aws:lambda:)?([a-z]{2}-[a-z]+-\d{1}:)?(\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\$LATEST|[a-zA-Z0-9-_]+))?$`

func resourceAwsLambdaPermission() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsLambdaPermissionCreate,
		Read:   resourceAwsLambdaPermissionRead,
		Delete: resourceAwsLambdaPermissionDelete,

		Schema: map[string]*schema.Schema{
			"action": &schema.Schema{
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateLambdaPermissionAction,
			},
			"function_name": &schema.Schema{
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateLambdaFunctionName,
			},
			"principal": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"qualifier": &schema.Schema{
				Type:         schema.TypeString,
				Optional:     true,
				ForceNew:     true,
				ValidateFunc: validateLambdaQualifier,
			},
			"source_account": &schema.Schema{
				Type:         schema.TypeString,
				Optional:     true,
				ForceNew:     true,
				ValidateFunc: validateAwsAccountId,
			},
			"source_arn": &schema.Schema{
				Type:         schema.TypeString,
				Optional:     true,
				ForceNew:     true,
				ValidateFunc: validateArn,
			},
			"statement_id": &schema.Schema{
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validatePolicyStatementId,
			},
		},
	}
}

func resourceAwsLambdaPermissionCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).lambdaconn

	functionName := d.Get("function_name").(string)

	// There is a bug in the API (reported and acknowledged by AWS)
	// which causes some permissions to be ignored when API calls are sent in parallel
	// We work around this bug via mutex
	awsMutexKV.Lock(functionName)
	defer awsMutexKV.Unlock(functionName)

	input := lambda.AddPermissionInput{
		Action:       aws.String(d.Get("action").(string)),
		FunctionName: aws.String(functionName),
		Principal:    aws.String(d.Get("principal").(string)),
		StatementId:  aws.String(d.Get("statement_id").(string)),
	}

	if v, ok := d.GetOk("qualifier"); ok {
		input.Qualifier = aws.String(v.(string))
	}
	if v, ok := d.GetOk("source_account"); ok {
		input.SourceAccount = aws.String(v.(string))
	}
	if v, ok := d.GetOk("source_arn"); ok {
		input.SourceArn = aws.String(v.(string))
	}

	log.Printf("[DEBUG] Adding new Lambda permission: %s", input)
	var out *lambda.AddPermissionOutput
	err := resource.Retry(1*time.Minute, func() *resource.RetryError {
		var err error
		out, err = conn.AddPermission(&input)

		if err != nil {
			if awsErr, ok := err.(awserr.Error); ok {
				// IAM is eventually consistent :/
				if awsErr.Code() == "ResourceConflictException" {
					return resource.RetryableError(
						fmt.Errorf("[WARN] Error adding new Lambda Permission for %s, retrying: %s",
							*input.FunctionName, err))
				}
			}
			return resource.NonRetryableError(err)
		}
		return nil
	})

	if err != nil {
		return err
	}

	log.Printf("[DEBUG] Created new Lambda permission: %s", *out.Statement)

	d.SetId(d.Get("statement_id").(string))

	err = resource.Retry(5*time.Minute, func() *resource.RetryError {
		// IAM is eventually cosistent :/
		err := resourceAwsLambdaPermissionRead(d, meta)
		if err != nil {
			if strings.HasPrefix(err.Error(), "Error reading Lambda policy: ResourceNotFoundException") {
				return resource.RetryableError(
					fmt.Errorf("[WARN] Error reading newly created Lambda Permission for %s, retrying: %s",
						*input.FunctionName, err))
			}
			if strings.HasPrefix(err.Error(), "Failed to find statement \""+d.Id()) {
				return resource.RetryableError(
					fmt.Errorf("[WARN] Error reading newly created Lambda Permission statement for %s, retrying: %s",
						*input.FunctionName, err))
			}

			log.Printf("[ERROR] An actual error occured when expecting Lambda policy to be there: %s", err)
			return resource.NonRetryableError(err)
		}
		return nil
	})

	return err
}

func resourceAwsLambdaPermissionRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).lambdaconn

	input := lambda.GetPolicyInput{
		FunctionName: aws.String(d.Get("function_name").(string)),
	}
	if v, ok := d.GetOk("qualifier"); ok {
		input.Qualifier = aws.String(v.(string))
	}

	log.Printf("[DEBUG] Looking for Lambda permission: %s", input)
	var out *lambda.GetPolicyOutput
	var statement *LambdaPolicyStatement
	err := resource.Retry(1*time.Minute, func() *resource.RetryError {
		// IAM is eventually cosistent :/
		var err error
		out, err = conn.GetPolicy(&input)
		if err != nil {
			if awsErr, ok := err.(awserr.Error); ok {
				if awsErr.Code() == "ResourceNotFoundException" {
					return resource.RetryableError(err)
				}
			}
			return resource.NonRetryableError(err)
		}

		policyInBytes := []byte(*out.Policy)
		policy := LambdaPolicy{}
		err = json.Unmarshal(policyInBytes, &policy)
		if err != nil {
			return resource.NonRetryableError(err)
		}

		statement, err = findLambdaPolicyStatementById(&policy, d.Id())
		return resource.RetryableError(err)
	})
	if err != nil {
		return err
	}

	qualifier, err := getQualifierFromLambdaAliasOrVersionArn(statement.Resource)
	if err == nil {
		d.Set("qualifier", qualifier)
	}

	// Save Lambda function name in the same format
	if strings.HasPrefix(d.Get("function_name").(string), "arn:aws:lambda:") {
		// Strip qualifier off
		trimmedArn := strings.TrimSuffix(statement.Resource, ":"+qualifier)
		d.Set("function_name", trimmedArn)
	} else {
		functionName, err := getFunctionNameFromLambdaArn(statement.Resource)
		if err != nil {
			return err
		}
		d.Set("function_name", functionName)
	}

	d.Set("action", statement.Action)
	d.Set("principal", statement.Principal["Service"])

	if stringEquals, ok := statement.Condition["StringEquals"]; ok {
		d.Set("source_account", stringEquals["AWS:SourceAccount"])
	}

	if arnLike, ok := statement.Condition["ArnLike"]; ok {
		d.Set("source_arn", arnLike["AWS:SourceArn"])
	}

	return nil
}

func resourceAwsLambdaPermissionDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).lambdaconn

	functionName := d.Get("function_name").(string)

	// There is a bug in the API (reported and acknowledged by AWS)
	// which causes some permissions to be ignored when API calls are sent in parallel
	// We work around this bug via mutex
	awsMutexKV.Lock(functionName)
	defer awsMutexKV.Unlock(functionName)

	input := lambda.RemovePermissionInput{
		FunctionName: aws.String(functionName),
		StatementId:  aws.String(d.Id()),
	}

	if v, ok := d.GetOk("qualifier"); ok {
		input.Qualifier = aws.String(v.(string))
	}

	log.Printf("[DEBUG] Removing Lambda permission: %s", input)
	_, err := conn.RemovePermission(&input)
	if err != nil {
		return err
	}

	err = resource.Retry(5*time.Minute, func() *resource.RetryError {
		log.Printf("[DEBUG] Checking if Lambda permission %q is deleted", d.Id())

		params := &lambda.GetPolicyInput{
			FunctionName: aws.String(d.Get("function_name").(string)),
		}
		if v, ok := d.GetOk("qualifier"); ok {
			params.Qualifier = aws.String(v.(string))
		}

		log.Printf("[DEBUG] Looking for Lambda permission: %s", *params)
		resp, err := conn.GetPolicy(params)
		if err != nil {
			if awsErr, ok := err.(awserr.Error); ok {
				if awsErr.Code() == "ResourceNotFoundException" {
					return nil
				}
			}
			return resource.NonRetryableError(err)
		}

		if resp.Policy == nil {
			return nil
		}

		policyInBytes := []byte(*resp.Policy)
		policy := LambdaPolicy{}
		err = json.Unmarshal(policyInBytes, &policy)
		if err != nil {
			return resource.RetryableError(
				fmt.Errorf("Error unmarshalling Lambda policy: %s", err))
		}

		_, err = findLambdaPolicyStatementById(&policy, d.Id())
		if err != nil {
			return nil
		}

		log.Printf("[DEBUG] No error when checking if Lambda permission %s is deleted", d.Id())
		return nil
	})

	if err != nil {
		return fmt.Errorf("Failed removing Lambda permission: %s", err)
	}

	log.Printf("[DEBUG] Lambda permission with ID %q removed", d.Id())
	d.SetId("")

	return nil
}

func findLambdaPolicyStatementById(policy *LambdaPolicy, id string) (
	*LambdaPolicyStatement, error) {

	log.Printf("[DEBUG] Received %d statements in Lambda policy: %s", len(policy.Statement), policy.Statement)
	for _, statement := range policy.Statement {
		if statement.Sid == id {
			return &statement, nil
		}
	}

	return nil, fmt.Errorf("Failed to find statement %q in Lambda policy:\n%s", id, policy.Statement)
}

func getQualifierFromLambdaAliasOrVersionArn(arn string) (string, error) {
	matches := regexp.MustCompile(LambdaFunctionRegexp).FindStringSubmatch(arn)
	if len(matches) < 8 || matches[7] == "" {
		return "", fmt.Errorf("Invalid ARN or otherwise unable to get qualifier from ARN (%q)",
			arn)
	}

	return matches[7], nil
}

func getFunctionNameFromLambdaArn(arn string) (string, error) {
	matches := regexp.MustCompile(LambdaFunctionRegexp).FindStringSubmatch(arn)
	if len(matches) < 6 || matches[5] == "" {
		return "", fmt.Errorf("Invalid ARN or otherwise unable to get qualifier from ARN (%q)",
			arn)
	}
	return matches[5], nil
}

type LambdaPolicy struct {
	Version   string
	Statement []LambdaPolicyStatement
	Id        string
}

type LambdaPolicyStatement struct {
	Condition map[string]map[string]string
	Action    string
	Resource  string
	Effect    string
	Principal map[string]string
	Sid       string
}
<commit_msg>provider/aws: Guard against empty responses from Lambda Permissions<commit_after>package aws

import (
	"encoding/json"
	"fmt"
	"log"
	"regexp"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/lambda"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

var LambdaFunctionRegexp = `^(arn:aws:lambda:)?([a-z]{2}-[a-z]+-\d{1}:)?(\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\$LATEST|[a-zA-Z0-9-_]+))?$`

func resourceAwsLambdaPermission() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsLambdaPermissionCreate,
		Read:   resourceAwsLambdaPermissionRead,
		Delete: resourceAwsLambdaPermissionDelete,

		Schema: map[string]*schema.Schema{
			"action": &schema.Schema{
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateLambdaPermissionAction,
			},
			"function_name": &schema.Schema{
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateLambdaFunctionName,
			},
			"principal": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"qualifier": &schema.Schema{
				Type:         schema.TypeString,
				Optional:     true,
				ForceNew:     true,
				ValidateFunc: validateLambdaQualifier,
			},
			"source_account": &schema.Schema{
				Type:         schema.TypeString,
				Optional:     true,
				ForceNew:     true,
				ValidateFunc: validateAwsAccountId,
			},
			"source_arn": &schema.Schema{
				Type:         schema.TypeString,
				Optional:     true,
				ForceNew:     true,
				ValidateFunc: validateArn,
			},
			"statement_id": &schema.Schema{
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validatePolicyStatementId,
			},
		},
	}
}

func resourceAwsLambdaPermissionCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).lambdaconn

	functionName := d.Get("function_name").(string)

	// There is a bug in the API (reported and acknowledged by AWS)
	// which causes some permissions to be ignored when API calls are sent in parallel
	// We work around this bug via mutex
	awsMutexKV.Lock(functionName)
	defer awsMutexKV.Unlock(functionName)

	input := lambda.AddPermissionInput{
		Action:       aws.String(d.Get("action").(string)),
		FunctionName: aws.String(functionName),
		Principal:    aws.String(d.Get("principal").(string)),
		StatementId:  aws.String(d.Get("statement_id").(string)),
	}

	if v, ok := d.GetOk("qualifier"); ok {
		input.Qualifier = aws.String(v.(string))
	}
	if v, ok := d.GetOk("source_account"); ok {
		input.SourceAccount = aws.String(v.(string))
	}
	if v, ok := d.GetOk("source_arn"); ok {
		input.SourceArn = aws.String(v.(string))
	}

	log.Printf("[DEBUG] Adding new Lambda permission: %s", input)
	var out *lambda.AddPermissionOutput
	err := resource.Retry(1*time.Minute, func() *resource.RetryError {
		var err error
		out, err = conn.AddPermission(&input)

		if err != nil {
			if awsErr, ok := err.(awserr.Error); ok {
				// IAM is eventually consistent :/
				if awsErr.Code() == "ResourceConflictException" {
					return resource.RetryableError(
						fmt.Errorf("[WARN] Error adding new Lambda Permission for %s, retrying: %s",
							*input.FunctionName, err))
				}
			}
			return resource.NonRetryableError(err)
		}
		return nil
	})

	if err != nil {
		return err
	}

	if out != nil && out.Statement != nil {
		log.Printf("[DEBUG] Created new Lambda permission: %s", *out.Statement)
	} else {
		log.Printf("[DEBUG] Created new Lambda permission, but no Statement was included")
	}

	d.SetId(d.Get("statement_id").(string))

	err = resource.Retry(5*time.Minute, func() *resource.RetryError {
		// IAM is eventually cosistent :/
		err := resourceAwsLambdaPermissionRead(d, meta)
		if err != nil {
			if strings.HasPrefix(err.Error(), "Error reading Lambda policy: ResourceNotFoundException") {
				return resource.RetryableError(
					fmt.Errorf("[WARN] Error reading newly created Lambda Permission for %s, retrying: %s",
						*input.FunctionName, err))
			}
			if strings.HasPrefix(err.Error(), "Failed to find statement \""+d.Id()) {
				return resource.RetryableError(
					fmt.Errorf("[WARN] Error reading newly created Lambda Permission statement for %s, retrying: %s",
						*input.FunctionName, err))
			}

			log.Printf("[ERROR] An actual error occured when expecting Lambda policy to be there: %s", err)
			return resource.NonRetryableError(err)
		}
		return nil
	})

	return err
}

func resourceAwsLambdaPermissionRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).lambdaconn

	input := lambda.GetPolicyInput{
		FunctionName: aws.String(d.Get("function_name").(string)),
	}
	if v, ok := d.GetOk("qualifier"); ok {
		input.Qualifier = aws.String(v.(string))
	}

	log.Printf("[DEBUG] Looking for Lambda permission: %s", input)
	var out *lambda.GetPolicyOutput
	var statement *LambdaPolicyStatement
	err := resource.Retry(1*time.Minute, func() *resource.RetryError {
		// IAM is eventually cosistent :/
		var err error
		out, err = conn.GetPolicy(&input)
		if err != nil {
			if awsErr, ok := err.(awserr.Error); ok {
				if awsErr.Code() == "ResourceNotFoundException" {
					return resource.RetryableError(err)
				}
			}
			return resource.NonRetryableError(err)
		}

		policyInBytes := []byte(*out.Policy)
		policy := LambdaPolicy{}
		err = json.Unmarshal(policyInBytes, &policy)
		if err != nil {
			return resource.NonRetryableError(err)
		}

		statement, err = findLambdaPolicyStatementById(&policy, d.Id())
		return resource.RetryableError(err)
	})
	if err != nil {
		return err
	}

	qualifier, err := getQualifierFromLambdaAliasOrVersionArn(statement.Resource)
	if err == nil {
		d.Set("qualifier", qualifier)
	}

	// Save Lambda function name in the same format
	if strings.HasPrefix(d.Get("function_name").(string), "arn:aws:lambda:") {
		// Strip qualifier off
		trimmedArn := strings.TrimSuffix(statement.Resource, ":"+qualifier)
		d.Set("function_name", trimmedArn)
	} else {
		functionName, err := getFunctionNameFromLambdaArn(statement.Resource)
		if err != nil {
			return err
		}
		d.Set("function_name", functionName)
	}

	d.Set("action", statement.Action)
	d.Set("principal", statement.Principal["Service"])

	if stringEquals, ok := statement.Condition["StringEquals"]; ok {
		d.Set("source_account", stringEquals["AWS:SourceAccount"])
	}

	if arnLike, ok := statement.Condition["ArnLike"]; ok {
		d.Set("source_arn", arnLike["AWS:SourceArn"])
	}

	return nil
}

func resourceAwsLambdaPermissionDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).lambdaconn

	functionName := d.Get("function_name").(string)

	// There is a bug in the API (reported and acknowledged by AWS)
	// which causes some permissions to be ignored when API calls are sent in parallel
	// We work around this bug via mutex
	awsMutexKV.Lock(functionName)
	defer awsMutexKV.Unlock(functionName)

	input := lambda.RemovePermissionInput{
		FunctionName: aws.String(functionName),
		StatementId:  aws.String(d.Id()),
	}

	if v, ok := d.GetOk("qualifier"); ok {
		input.Qualifier = aws.String(v.(string))
	}

	log.Printf("[DEBUG] Removing Lambda permission: %s", input)
	_, err := conn.RemovePermission(&input)
	if err != nil {
		return err
	}

	err = resource.Retry(5*time.Minute, func() *resource.RetryError {
		log.Printf("[DEBUG] Checking if Lambda permission %q is deleted", d.Id())

		params := &lambda.GetPolicyInput{
			FunctionName: aws.String(d.Get("function_name").(string)),
		}
		if v, ok := d.GetOk("qualifier"); ok {
			params.Qualifier = aws.String(v.(string))
		}

		log.Printf("[DEBUG] Looking for Lambda permission: %s", *params)
		resp, err := conn.GetPolicy(params)
		if err != nil {
			if awsErr, ok := err.(awserr.Error); ok {
				if awsErr.Code() == "ResourceNotFoundException" {
					return nil
				}
			}
			return resource.NonRetryableError(err)
		}

		if resp.Policy == nil {
			return nil
		}

		policyInBytes := []byte(*resp.Policy)
		policy := LambdaPolicy{}
		err = json.Unmarshal(policyInBytes, &policy)
		if err != nil {
			return resource.RetryableError(
				fmt.Errorf("Error unmarshalling Lambda policy: %s", err))
		}

		_, err = findLambdaPolicyStatementById(&policy, d.Id())
		if err != nil {
			return nil
		}

		log.Printf("[DEBUG] No error when checking if Lambda permission %s is deleted", d.Id())
		return nil
	})

	if err != nil {
		return fmt.Errorf("Failed removing Lambda permission: %s", err)
	}

	log.Printf("[DEBUG] Lambda permission with ID %q removed", d.Id())
	d.SetId("")

	return nil
}

func findLambdaPolicyStatementById(policy *LambdaPolicy, id string) (
	*LambdaPolicyStatement, error) {

	log.Printf("[DEBUG] Received %d statements in Lambda policy: %s", len(policy.Statement), policy.Statement)
	for _, statement := range policy.Statement {
		if statement.Sid == id {
			return &statement, nil
		}
	}

	return nil, fmt.Errorf("Failed to find statement %q in Lambda policy:\n%s", id, policy.Statement)
}

func getQualifierFromLambdaAliasOrVersionArn(arn string) (string, error) {
	matches := regexp.MustCompile(LambdaFunctionRegexp).FindStringSubmatch(arn)
	if len(matches) < 8 || matches[7] == "" {
		return "", fmt.Errorf("Invalid ARN or otherwise unable to get qualifier from ARN (%q)",
			arn)
	}

	return matches[7], nil
}

func getFunctionNameFromLambdaArn(arn string) (string, error) {
	matches := regexp.MustCompile(LambdaFunctionRegexp).FindStringSubmatch(arn)
	if len(matches) < 6 || matches[5] == "" {
		return "", fmt.Errorf("Invalid ARN or otherwise unable to get qualifier from ARN (%q)",
			arn)
	}
	return matches[5], nil
}

type LambdaPolicy struct {
	Version   string
	Statement []LambdaPolicyStatement
	Id        string
}

type LambdaPolicyStatement struct {
	Condition map[string]map[string]string
	Action    string
	Resource  string
	Effect    string
	Principal map[string]string
	Sid       string
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Niklas Wolber
// This file is licensed under the MIT license.
// See the LICENSE file for more information.

package telemetry

import (
	"fmt"
	"io"
	"os"
	"runtime"
	"time"

	"github.com/nwolber/xCUTEr/logger"
)

type LogInfo struct {
	File, Message string
	Line          int
}

type telemetryLogger struct {
	orig   logger.Logger
	name   string
	events *EventStore
}

var (
	calldepth = 2
)

func (l *telemetryLogger) Log(t time.Time, message string) {
	_, file, line, ok := runtime.Caller(2)
	if !ok {
		file = "???"
	}

	if l := len(message); message[l-1] == '\n' {
		message = message[:l-1]
	}

	l.events.store(Event{
		Timestamp: t,
		Type:      EventLog,
		Name:      l.name,
		Info: LogInfo{
			File:    file,
			Line:    line,
			Message: message,
		},
	})
}

func (l *telemetryLogger) SetOutput(w io.Writer) {
	if l.orig != nil {
		l.orig.SetOutput(w)
	}
}

func (l *telemetryLogger) Flags() int {
	if l.orig == nil {
		return 0
	}
	return l.orig.Flags()
}

func (l *telemetryLogger) SetFlags(flag int) {
	if l.orig != nil {
		l.orig.SetFlags(flag)
	}
}

func (l *telemetryLogger) Prefix() string {
	if l.orig == nil {
		return ""
	}
	return l.orig.Prefix()
}

func (l *telemetryLogger) SetPrefix(prefix string) {
	if l.orig != nil {
		l.orig.SetPrefix(prefix)
	}
}

func (l *telemetryLogger) Print(v ...interface{}) {
	s := fmt.Sprint(v...)
	l.Log(time.Now(), s)
	if l.orig != nil {
		l.orig.Output(calldepth, s)
	}
}

func (l *telemetryLogger) Printf(format string, v ...interface{}) {
	s := fmt.Sprintf(format, v...)
	l.Log(time.Now(), s)
	if l.orig != nil {
		l.orig.Output(calldepth, s)
	}
}

func (l *telemetryLogger) Println(v ...interface{}) {
	s := fmt.Sprintln(v...)
	l.Log(time.Now(), s)
	if l.orig != nil {
		l.orig.Output(calldepth, s)
	}
}

func (l *telemetryLogger) Fatal(v ...interface{}) {
	s := fmt.Sprint(v...)
	l.Log(time.Now(), s)
	if l.orig != nil {
		l.orig.Output(calldepth, s)
		os.Exit(1)
	}
}

func (l *telemetryLogger) Fatalf(format string, v ...interface{}) {
	s := fmt.Sprintf(format, v...)
	l.Log(time.Now(), s)
	if l.orig != nil {
		l.orig.Output(calldepth, s)
		os.Exit(1)
	}
}

func (l *telemetryLogger) Fatalln(v ...interface{}) {
	s := fmt.Sprintln(v...)
	l.Log(time.Now(), s)
	if l.orig != nil {
		l.orig.Output(calldepth, s)
		os.Exit(1)
	}
}

func (l *telemetryLogger) Panic(v ...interface{}) {
	s := fmt.Sprint(v...)
	l.Log(time.Now(), s)
	if l.orig != nil {
		l.orig.Output(calldepth, s)
		panic(s)
	}
}

func (l *telemetryLogger) Panicf(format string, v ...interface{}) {
	s := fmt.Sprintf(format, v...)
	l.Log(time.Now(), s)
	if l.orig != nil {
		l.orig.Output(calldepth, s)
		panic(s)
	}
}

func (l *telemetryLogger) Panicln(v ...interface{}) {
	s := fmt.Sprintln(v...)
	l.Log(time.Now(), s)
	if l.orig != nil {
		l.orig.Output(calldepth, s)
		panic(s)
	}
}

func (l *telemetryLogger) Output(calldepth int, s string) error {
	if l.orig == nil {
		return nil
	}
	return l.orig.Output(calldepth+1, s)
}
<commit_msg>Only store file names in telemetry data<commit_after>// Copyright (c) 2016 Niklas Wolber
// This file is licensed under the MIT license.
// See the LICENSE file for more information.

package telemetry

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"runtime"
	"time"

	"github.com/nwolber/xCUTEr/logger"
)

type LogInfo struct {
	File, Message string
	Line          int
}

type telemetryLogger struct {
	orig   logger.Logger
	name   string
	events *EventStore
}

var (
	calldepth = 2
)

func (l *telemetryLogger) Log(t time.Time, message string) {
	_, file, line, ok := runtime.Caller(2)
	if !ok {
		file = "???"
	}

	_, file = filepath.Split(file)

	if l := len(message); message[l-1] == '\n' {
		message = message[:l-1]
	}

	l.events.store(Event{
		Timestamp: t,
		Type:      EventLog,
		Name:      l.name,
		Info: LogInfo{
			File:    file,
			Line:    line,
			Message: message,
		},
	})
}

func (l *telemetryLogger) SetOutput(w io.Writer) {
	if l.orig != nil {
		l.orig.SetOutput(w)
	}
}

func (l *telemetryLogger) Flags() int {
	if l.orig == nil {
		return 0
	}
	return l.orig.Flags()
}

func (l *telemetryLogger) SetFlags(flag int) {
	if l.orig != nil {
		l.orig.SetFlags(flag)
	}
}

func (l *telemetryLogger) Prefix() string {
	if l.orig == nil {
		return ""
	}
	return l.orig.Prefix()
}

func (l *telemetryLogger) SetPrefix(prefix string) {
	if l.orig != nil {
		l.orig.SetPrefix(prefix)
	}
}

func (l *telemetryLogger) Print(v ...interface{}) {
	s := fmt.Sprint(v...)
	l.Log(time.Now(), s)
	if l.orig != nil {
		l.orig.Output(calldepth, s)
	}
}

func (l *telemetryLogger) Printf(format string, v ...interface{}) {
	s := fmt.Sprintf(format, v...)
	l.Log(time.Now(), s)
	if l.orig != nil {
		l.orig.Output(calldepth, s)
	}
}

func (l *telemetryLogger) Println(v ...interface{}) {
	s := fmt.Sprintln(v...)
	l.Log(time.Now(), s)
	if l.orig != nil {
		l.orig.Output(calldepth, s)
	}
}

func (l *telemetryLogger) Fatal(v ...interface{}) {
	s := fmt.Sprint(v...)
	l.Log(time.Now(), s)
	if l.orig != nil {
		l.orig.Output(calldepth, s)
		os.Exit(1)
	}
}

func (l *telemetryLogger) Fatalf(format string, v ...interface{}) {
	s := fmt.Sprintf(format, v...)
	l.Log(time.Now(), s)
	if l.orig != nil {
		l.orig.Output(calldepth, s)
		os.Exit(1)
	}
}

func (l *telemetryLogger) Fatalln(v ...interface{}) {
	s := fmt.Sprintln(v...)
	l.Log(time.Now(), s)
	if l.orig != nil {
		l.orig.Output(calldepth, s)
		os.Exit(1)
	}
}

func (l *telemetryLogger) Panic(v ...interface{}) {
	s := fmt.Sprint(v...)
	l.Log(time.Now(), s)
	if l.orig != nil {
		l.orig.Output(calldepth, s)
		panic(s)
	}
}

func (l *telemetryLogger) Panicf(format string, v ...interface{}) {
	s := fmt.Sprintf(format, v...)
	l.Log(time.Now(), s)
	if l.orig != nil {
		l.orig.Output(calldepth, s)
		panic(s)
	}
}

func (l *telemetryLogger) Panicln(v ...interface{}) {
	s := fmt.Sprintln(v...)
	l.Log(time.Now(), s)
	if l.orig != nil {
		l.orig.Output(calldepth, s)
		panic(s)
	}
}

func (l *telemetryLogger) Output(calldepth int, s string) error {
	if l.orig == nil {
		return nil
	}
	return l.orig.Output(calldepth+1, s)
}
<|endoftext|>


<commit_before>package webapi

// webapiだお

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/RobotClubKut/SlackBot/lib/conf"
	"github.com/RobotClubKut/SlackBot/lib/mysql"
)

func home(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello world")
}

func deny(w http.ResponseWriter, r *http.Request) {
	configure := conf.ReadConfigure()
	text := r.PostFormValue("text")
	token := r.PostFormValue("token")
	userName := r.PostFormValue("user_name")
	fmt.Println("command flag:", strings.Contains(text, "deny:"))
	fmt.Println("token check:", configure.OutgoingSlackConf.Token == token)
	fmt.Println("user name:", configure.OutgoingSlackConf.UserName != userName)
	if strings.Contains(text, "deny:") && configure.OutgoingSlackConf.Token == token && configure.OutgoingSlackConf.UserName != userName {
		fmt.Println("catch")
		text = strings.Replace(text, "deny:", "", 0)
		fmt.Println("deny 除去", text)
		text = strings.Replace(text, " ", "", 0)
		fmt.Println("space 除去", text)
		words := strings.Split(text, ":")
		fmt.Println("words:", words)
		mysql.InsertDenyWord(words)
		//postText := "{\"text\":\"" +  + ""\"}"
		postText := "{\"text\":\""
		for _, w := range words {
			if w != "" {
				postText += w
				postText += ","
			}
		}
		postText += "\"}"
		fmt.Fprintf(w, postText)
	} else {
		if configure.OutgoingSlackConf.UserName == userName {

		} else {
			postText := "{\"text\":\"nilぱすー\"}"
			fmt.Fprintf(w, postText)
		}
	}
}

//ViewWebPage is view web api
func ViewWebPage() {
	configure := conf.ReadConfigure()
	http.HandleFunc("/", home)
	http.HandleFunc("/deny", deny)
	http.ListenAndServe(":"+configure.OutgoingSlackConf.Port, nil)
}
<commit_msg>文字列のゴミ取り<commit_after>package webapi

// webapiだお

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/RobotClubKut/SlackBot/lib/conf"
	"github.com/RobotClubKut/SlackBot/lib/mysql"
)

func home(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello world")
}

func deny(w http.ResponseWriter, r *http.Request) {
	configure := conf.ReadConfigure()
	text := r.PostFormValue("text")
	token := r.PostFormValue("token")
	userName := r.PostFormValue("user_name")
	fmt.Println("command flag:", strings.Contains(text, "deny:"))
	fmt.Println("token check:", configure.OutgoingSlackConf.Token == token)
	fmt.Println("user name:", configure.OutgoingSlackConf.UserName != userName)
	if strings.Contains(text, "deny:") && configure.OutgoingSlackConf.Token == token && configure.OutgoingSlackConf.UserName != userName {
		fmt.Println("catch")
		text = strings.Replace(text, "deny:", " ", 0)
		fmt.Println("deny 除去", text)
		words := strings.Split(text, ":")
		var buf []string
		for _, w := range words {
			buf = append(buf, strings.Split(w, " ")...)
		}
		words = buf
		fmt.Println("words:", words)
		mysql.InsertDenyWord(words)
		//postText := "{\"text\":\"" +  + ""\"}"
		postText := "{\"text\":\""
		for _, w := range words {
			if w != "" {
				postText += w
				postText += ","
			}
		}
		postText += "\"}"
		fmt.Fprintf(w, postText)
	} else {
		if configure.OutgoingSlackConf.UserName == userName {

		} else {
			postText := "{\"text\":\"nilぱすー\"}"
			fmt.Fprintf(w, postText)
		}
	}
}

//ViewWebPage is view web api
func ViewWebPage() {
	configure := conf.ReadConfigure()
	http.HandleFunc("/", home)
	http.HandleFunc("/deny", deny)
	http.ListenAndServe(":"+configure.OutgoingSlackConf.Port, nil)
}
<|endoftext|>


<commit_before>package storage

import (
	"bytes"
	"io"
	"path"
	"sort"
	"strconv"
	"sync"

	"github.com/anacrolix/missinggo/v2/resource"

	"github.com/anacrolix/torrent/metainfo"
)

type piecePerResource struct {
	rp   PieceProvider
	opts ResourcePiecesOpts
}

type ResourcePiecesOpts struct {
	LeaveIncompleteChunks bool
	AllowSizedPuts        bool
}

func NewResourcePieces(p PieceProvider) ClientImpl {
	return NewResourcePiecesOpts(p, ResourcePiecesOpts{})
}

func NewResourcePiecesOpts(p PieceProvider, opts ResourcePiecesOpts) ClientImpl {
	return &piecePerResource{
		rp: p,
	}
}

type piecePerResourceTorrentImpl struct {
	piecePerResource
}

func (piecePerResourceTorrentImpl) Close() error {
	return nil
}

func (s piecePerResource) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (TorrentImpl, error) {
	return piecePerResourceTorrentImpl{s}, nil
}

func (s piecePerResource) Piece(p metainfo.Piece) PieceImpl {
	return piecePerResourcePiece{
		mp:               p,
		piecePerResource: s,
	}
}

type PieceProvider interface {
	resource.Provider
}

type ConsecutiveChunkWriter interface {
	WriteConsecutiveChunks(prefix string, _ io.Writer) (int64, error)
}

type piecePerResourcePiece struct {
	mp metainfo.Piece
	piecePerResource
}

var _ io.WriterTo = piecePerResourcePiece{}

func (s piecePerResourcePiece) WriteTo(w io.Writer) (int64, error) {
	if ccw, ok := s.rp.(ConsecutiveChunkWriter); ok {
		if s.mustIsComplete() {
			return ccw.WriteConsecutiveChunks(s.completedInstancePath(), w)
		} else {
			return s.writeConsecutiveIncompleteChunks(ccw, w)
		}
	}
	return io.Copy(w, io.NewSectionReader(s, 0, s.mp.Length()))
}

func (s piecePerResourcePiece) writeConsecutiveIncompleteChunks(ccw ConsecutiveChunkWriter, w io.Writer) (int64, error) {
	return ccw.WriteConsecutiveChunks(s.incompleteDirPath()+"/", w)
}

// Returns if the piece is complete. Ok should be true, because we are the definitive source of
// truth here.
func (s piecePerResourcePiece) mustIsComplete() bool {
	completion := s.Completion()
	if !completion.Ok {
		panic("must know complete definitively")
	}
	return completion.Complete
}

func (s piecePerResourcePiece) Completion() Completion {
	fi, err := s.completed().Stat()
	return Completion{
		Complete: err == nil && fi.Size() == s.mp.Length(),
		Ok:       true,
	}
}

type SizedPutter interface {
	PutSized(io.Reader, int64) error
}

func (s piecePerResourcePiece) MarkComplete() error {
	incompleteChunks := s.getChunks()
	r, w := io.Pipe()
	go func() {
		var err error
		if ccw, ok := s.rp.(ConsecutiveChunkWriter); ok {
			_, err = s.writeConsecutiveIncompleteChunks(ccw, w)
		} else {
			_, err = io.Copy(w, io.NewSectionReader(incompleteChunks, 0, s.mp.Length()))
		}
		w.CloseWithError(err)
	}()
	completedInstance := s.completed()
	err := func() error {
		if sp, ok := completedInstance.(SizedPutter); ok && s.opts.AllowSizedPuts {
			return sp.PutSized(r, s.mp.Length())
		} else {
			return completedInstance.Put(r)
		}
	}()
	if err == nil && !s.opts.LeaveIncompleteChunks {
		// I think we do this synchronously here since we don't want callers to act on the completed
		// piece if we're concurrently still deleting chunks. The caller may decide to start
		// downloading chunks again and won't expect us to delete them. It seems to be much faster
		// to let the resource provider do this if possible.
		var wg sync.WaitGroup
		for _, c := range incompleteChunks {
			wg.Add(1)
			go func(c chunk) {
				defer wg.Done()
				c.instance.Delete()
			}(c)
		}
		wg.Wait()
	}
	return err
}

func (s piecePerResourcePiece) MarkNotComplete() error {
	return s.completed().Delete()
}

func (s piecePerResourcePiece) ReadAt(b []byte, off int64) (int, error) {
	if s.mustIsComplete() {
		return s.completed().ReadAt(b, off)
	}
	return s.getChunks().ReadAt(b, off)
}

func (s piecePerResourcePiece) WriteAt(b []byte, off int64) (n int, err error) {
	i, err := s.rp.NewInstance(path.Join(s.incompleteDirPath(), strconv.FormatInt(off, 10)))
	if err != nil {
		panic(err)
	}
	r := bytes.NewReader(b)
	err = i.Put(r)
	n = len(b) - r.Len()
	return
}

type chunk struct {
	offset   int64
	instance resource.Instance
}

type chunks []chunk

func (me chunks) ReadAt(b []byte, off int64) (int, error) {
	for {
		if len(me) == 0 {
			return 0, io.EOF
		}
		if me[0].offset <= off {
			break
		}
		me = me[1:]
	}
	n, err := me[0].instance.ReadAt(b, off-me[0].offset)
	if n == len(b) {
		return n, nil
	}
	if err == nil || err == io.EOF {
		n_, err := me[1:].ReadAt(b[n:], off+int64(n))
		return n + n_, err
	}
	return n, err
}

func (s piecePerResourcePiece) getChunks() (chunks chunks) {
	names, err := s.incompleteDir().Readdirnames()
	if err != nil {
		return
	}
	for _, n := range names {
		offset, err := strconv.ParseInt(n, 10, 64)
		if err != nil {
			panic(err)
		}
		i, err := s.rp.NewInstance(path.Join(s.incompleteDirPath(), n))
		if err != nil {
			panic(err)
		}
		chunks = append(chunks, chunk{offset, i})
	}
	sort.Slice(chunks, func(i, j int) bool {
		return chunks[i].offset < chunks[j].offset
	})
	return
}

func (s piecePerResourcePiece) completedInstancePath() string {
	return path.Join("completed", s.mp.Hash().HexString())
}

func (s piecePerResourcePiece) completed() resource.Instance {
	i, err := s.rp.NewInstance(s.completedInstancePath())
	if err != nil {
		panic(err)
	}
	return i
}

func (s piecePerResourcePiece) incompleteDirPath() string {
	return path.Join("incompleted", s.mp.Hash().HexString())
}

func (s piecePerResourcePiece) incompleteDir() resource.DirInstance {
	i, err := s.rp.NewInstance(s.incompleteDirPath())
	if err != nil {
		panic(err)
	}
	return i.(resource.DirInstance)
}
<commit_msg>Fix unused opts param<commit_after>package storage

import (
	"bytes"
	"io"
	"path"
	"sort"
	"strconv"
	"sync"

	"github.com/anacrolix/missinggo/v2/resource"

	"github.com/anacrolix/torrent/metainfo"
)

type piecePerResource struct {
	rp   PieceProvider
	opts ResourcePiecesOpts
}

type ResourcePiecesOpts struct {
	LeaveIncompleteChunks bool
	AllowSizedPuts        bool
}

func NewResourcePieces(p PieceProvider) ClientImpl {
	return NewResourcePiecesOpts(p, ResourcePiecesOpts{})
}

func NewResourcePiecesOpts(p PieceProvider, opts ResourcePiecesOpts) ClientImpl {
	return &piecePerResource{
		rp:   p,
		opts: opts,
	}
}

type piecePerResourceTorrentImpl struct {
	piecePerResource
}

func (piecePerResourceTorrentImpl) Close() error {
	return nil
}

func (s piecePerResource) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (TorrentImpl, error) {
	return piecePerResourceTorrentImpl{s}, nil
}

func (s piecePerResource) Piece(p metainfo.Piece) PieceImpl {
	return piecePerResourcePiece{
		mp:               p,
		piecePerResource: s,
	}
}

type PieceProvider interface {
	resource.Provider
}

type ConsecutiveChunkWriter interface {
	WriteConsecutiveChunks(prefix string, _ io.Writer) (int64, error)
}

type piecePerResourcePiece struct {
	mp metainfo.Piece
	piecePerResource
}

var _ io.WriterTo = piecePerResourcePiece{}

func (s piecePerResourcePiece) WriteTo(w io.Writer) (int64, error) {
	if ccw, ok := s.rp.(ConsecutiveChunkWriter); ok {
		if s.mustIsComplete() {
			return ccw.WriteConsecutiveChunks(s.completedInstancePath(), w)
		} else {
			return s.writeConsecutiveIncompleteChunks(ccw, w)
		}
	}
	return io.Copy(w, io.NewSectionReader(s, 0, s.mp.Length()))
}

func (s piecePerResourcePiece) writeConsecutiveIncompleteChunks(ccw ConsecutiveChunkWriter, w io.Writer) (int64, error) {
	return ccw.WriteConsecutiveChunks(s.incompleteDirPath()+"/", w)
}

// Returns if the piece is complete. Ok should be true, because we are the definitive source of
// truth here.
func (s piecePerResourcePiece) mustIsComplete() bool {
	completion := s.Completion()
	if !completion.Ok {
		panic("must know complete definitively")
	}
	return completion.Complete
}

func (s piecePerResourcePiece) Completion() Completion {
	fi, err := s.completed().Stat()
	return Completion{
		Complete: err == nil && fi.Size() == s.mp.Length(),
		Ok:       true,
	}
}

type SizedPutter interface {
	PutSized(io.Reader, int64) error
}

func (s piecePerResourcePiece) MarkComplete() error {
	incompleteChunks := s.getChunks()
	r, w := io.Pipe()
	go func() {
		var err error
		if ccw, ok := s.rp.(ConsecutiveChunkWriter); ok {
			_, err = s.writeConsecutiveIncompleteChunks(ccw, w)
		} else {
			_, err = io.Copy(w, io.NewSectionReader(incompleteChunks, 0, s.mp.Length()))
		}
		w.CloseWithError(err)
	}()
	completedInstance := s.completed()
	err := func() error {
		if sp, ok := completedInstance.(SizedPutter); ok && s.opts.AllowSizedPuts {
			return sp.PutSized(r, s.mp.Length())
		} else {
			return completedInstance.Put(r)
		}
	}()
	if err == nil && !s.opts.LeaveIncompleteChunks {
		// I think we do this synchronously here since we don't want callers to act on the completed
		// piece if we're concurrently still deleting chunks. The caller may decide to start
		// downloading chunks again and won't expect us to delete them. It seems to be much faster
		// to let the resource provider do this if possible.
		var wg sync.WaitGroup
		for _, c := range incompleteChunks {
			wg.Add(1)
			go func(c chunk) {
				defer wg.Done()
				c.instance.Delete()
			}(c)
		}
		wg.Wait()
	}
	return err
}

func (s piecePerResourcePiece) MarkNotComplete() error {
	return s.completed().Delete()
}

func (s piecePerResourcePiece) ReadAt(b []byte, off int64) (int, error) {
	if s.mustIsComplete() {
		return s.completed().ReadAt(b, off)
	}
	return s.getChunks().ReadAt(b, off)
}

func (s piecePerResourcePiece) WriteAt(b []byte, off int64) (n int, err error) {
	i, err := s.rp.NewInstance(path.Join(s.incompleteDirPath(), strconv.FormatInt(off, 10)))
	if err != nil {
		panic(err)
	}
	r := bytes.NewReader(b)
	err = i.Put(r)
	n = len(b) - r.Len()
	return
}

type chunk struct {
	offset   int64
	instance resource.Instance
}

type chunks []chunk

func (me chunks) ReadAt(b []byte, off int64) (int, error) {
	for {
		if len(me) == 0 {
			return 0, io.EOF
		}
		if me[0].offset <= off {
			break
		}
		me = me[1:]
	}
	n, err := me[0].instance.ReadAt(b, off-me[0].offset)
	if n == len(b) {
		return n, nil
	}
	if err == nil || err == io.EOF {
		n_, err := me[1:].ReadAt(b[n:], off+int64(n))
		return n + n_, err
	}
	return n, err
}

func (s piecePerResourcePiece) getChunks() (chunks chunks) {
	names, err := s.incompleteDir().Readdirnames()
	if err != nil {
		return
	}
	for _, n := range names {
		offset, err := strconv.ParseInt(n, 10, 64)
		if err != nil {
			panic(err)
		}
		i, err := s.rp.NewInstance(path.Join(s.incompleteDirPath(), n))
		if err != nil {
			panic(err)
		}
		chunks = append(chunks, chunk{offset, i})
	}
	sort.Slice(chunks, func(i, j int) bool {
		return chunks[i].offset < chunks[j].offset
	})
	return
}

func (s piecePerResourcePiece) completedInstancePath() string {
	return path.Join("completed", s.mp.Hash().HexString())
}

func (s piecePerResourcePiece) completed() resource.Instance {
	i, err := s.rp.NewInstance(s.completedInstancePath())
	if err != nil {
		panic(err)
	}
	return i
}

func (s piecePerResourcePiece) incompleteDirPath() string {
	return path.Join("incompleted", s.mp.Hash().HexString())
}

func (s piecePerResourcePiece) incompleteDir() resource.DirInstance {
	i, err := s.rp.NewInstance(s.incompleteDirPath())
	if err != nil {
		panic(err)
	}
	return i.(resource.DirInstance)
}
<|endoftext|>