add yaml negotitation (#2220)

<|original_code|>
			return true
		default:
			keepOpen := step(w)
			w.Flush()
			if !keepOpen {
				return false
			}
		}
	}
}

/************************************/
/******** CONTENT NEGOTIATION *******/
/************************************/

// Negotiate contains all negotiations data.
type Negotiate struct {
	Offered  []string
	HTMLName string
	HTMLData interface{}
	JSONData interface{}
	XMLData  interface{}
	Data     interface{}
}

// Negotiate calls different Render according acceptable Accept format.
func (c *Context) Negotiate(code int, config Negotiate) {
	switch c.NegotiateFormat(config.Offered...) {
	case binding.MIMEJSON:
		data := chooseData(config.JSONData, config.Data)
		c.JSON(code, data)

	case binding.MIMEHTML:
		data := chooseData(config.HTMLData, config.Data)
		c.HTML(code, config.HTMLName, data)

	case binding.MIMEXML:
		data := chooseData(config.XMLData, config.Data)
		c.XML(code, data)

	default:
		c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server")) // nolint: errcheck
	}
}

// NegotiateFormat returns an acceptable Accept format.
func (c *Context) NegotiateFormat(offered ...string) string {
	assert1(len(offered) > 0, "you must provide at least one offer")

	if c.Accepted == nil {
		c.Accepted = parseAccept(c.requestHeader("Accept"))
	}
	if len(c.Accepted) == 0 {
		return offered[0]
	}
	for _, accepted := range c.Accepted {
		for _, offer := range offered {
			// According to RFC 2616 and RFC 2396, non-ASCII characters are not allowed in headers,
			// therefore we can just iterate over the string without casting it into []rune
			i := 0
			for ; i < len(accepted); i++ {
				if accepted[i] == '*' || offer[i] == '*' {
					return offer
<|edits_diff|>
--- context.go
+++ context.go
@@ -20,6 +20,7 @@
 	HTMLData interface{}
 	JSONData interface{}
 	XMLData  interface{}
+	YAMLData interface{}
 	Data     interface{}
 }

<|current_version|>
			return true
		default:
			keepOpen := step(w)
			w.Flush()
			if !keepOpen {
				return false
			}
		}
	}
}

/************************************/
/******** CONTENT NEGOTIATION *******/
/************************************/

// Negotiate contains all negotiations data.
type Negotiate struct {
	Offered  []string
	HTMLName string
	HTMLData interface{}
	JSONData interface{}
	XMLData  interface{}
	YAMLData interface{}
	Data     interface{}
}

// Negotiate calls different Render according acceptable Accept format.
func (c *Context) Negotiate(code int, config Negotiate) {
	switch c.NegotiateFormat(config.Offered...) {
	case binding.MIMEJSON:
		data := chooseData(config.JSONData, config.Data)
		c.JSON(code, data)

	case binding.MIMEHTML:
		data := chooseData(config.HTMLData, config.Data)
		c.HTML(code, config.HTMLName, data)

	case binding.MIMEXML:
		data := chooseData(config.XMLData, config.Data)
		c.XML(code, data)

	default:
		c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server")) // nolint: errcheck
	}
}

// NegotiateFormat returns an acceptable Accept format.
func (c *Context) NegotiateFormat(offered ...string) string {
	assert1(len(offered) > 0, "you must provide at least one offer")

	if c.Accepted == nil {
		c.Accepted = parseAccept(c.requestHeader("Accept"))
	}
	if len(c.Accepted) == 0 {
		return offered[0]
	}
	for _, accepted := range c.Accepted {
		for _, offer := range offered {
			// According to RFC 2616 and RFC 2396, non-ASCII characters are not allowed in headers,
			// therefore we can just iterate over the string without casting it into []rune
			i := 0
			for ; i < len(accepted); i++ {
				if accepted[i] == '*' || offer[i] == '*' {
					return offer
<|next_version|>
			return true
		default:
			keepOpen := step(w)
			w.Flush()
			if !keepOpen {
				return false
			}
		}
	}
}

/************************************/
/******** CONTENT NEGOTIATION *******/
/************************************/

// Negotiate contains all negotiations data.
type Negotiate struct {
	Offered  []string
	HTMLName string
	HTMLData interface{}
	JSONData interface{}
	XMLData  interface{}
	YAMLData interface{}
	Data     interface{}
}

// Negotiate calls different Render according acceptable Accept format.
func (c *Context) Negotiate(code int, config Negotiate) {
	switch c.NegotiateFormat(config.Offered...) {
	case binding.MIMEJSON:
		data := chooseData(config.JSONData, config.Data)
		c.JSON(code, data)

	case binding.MIMEHTML:
		data := chooseData(config.HTMLData, config.Data)
		c.HTML(code, config.HTMLName, data)

	case binding.MIMEXML:
		data := chooseData(config.XMLData, config.Data)
		c.XML(code, data)

	case binding.MIMEYAML:
		data := chooseData(config.YAMLData, config.Data)
		c.YAML(code, data)

	default:
		c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server")) // nolint: errcheck
	}
}

// NegotiateFormat returns an acceptable Accept format.
func (c *Context) NegotiateFormat(offered ...string) string {
	assert1(len(offered) > 0, "you must provide at least one offer")

	if c.Accepted == nil {
		c.Accepted = parseAccept(c.requestHeader("Accept"))
	}
	if len(c.Accepted) == 0 {
		return offered[0]
	}
	for _, accepted := range c.Accepted {
		for _, offer := range offered {
			// According to RFC 2616 and RFC 2396, non-ASCII characters are not allowed in headers,
			// therefore we can just iterate over the string without casting it into []rune
			i := 0
			for ; i < len(accepted); i++ {
				if accepted[i] == '*' || offer[i] == '*' {
					return offer


Yaml binding (#1618)

<|original_code|>
// Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package binding

import "net/http"

// Content-Type MIME of the most common data formats.
const (
	MIMEJSON              = "application/json"
	MIMEHTML              = "text/html"
	MIMEXML               = "application/xml"
	MIMEXML2              = "text/xml"
	MIMEPlain             = "text/plain"
	MIMEPOSTForm          = "application/x-www-form-urlencoded"
	MIMEMultipartPOSTForm = "multipart/form-data"
	MIMEPROTOBUF          = "application/x-protobuf"
	MIMEMSGPACK           = "application/x-msgpack"
	MIMEMSGPACK2          = "application/msgpack"
)

// Binding describes the interface which needs to be implemented for binding the
// data present in the request such as JSON request body, query parameters or
// the form POST.
type Binding interface {
	Name() string
	Bind(*http.Request, interface{}) error
}

// BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
// but it reads the body from supplied bytes instead of req.Body.
type BindingBody interface {
	Binding
	BindBody([]byte, interface{}) error
}

// StructValidator is the minimal interface which needs to be implemented in
// order for it to be used as the validator engine for ensuring the correctness
// of the request. Gin provides a default implementation for this using
// https://github.com/go-playground/validator/tree/v8.18.2.
type StructValidator interface {
	// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
	// If the received type is not a struct, any validation should be skipped and nil must be returned.
	// If the received type is a struct or pointer to a struct, the validation should be performed.
	// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
	// Otherwise nil must be returned.
	ValidateStruct(interface{}) error

	// Engine returns the underlying validator engine which powers the
	// StructValidator implementation.
	Engine() interface{}
}

// Validator is the default validator which implements the StructValidator
// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2
// under the hood.
var Validator StructValidator = &defaultValidator{}

// These implement the Binding interface and can be used to bind the data
// present in the request to struct instances.
var (
	JSON          = jsonBinding{}
	XML           = xmlBinding{}
	Form          = formBinding{}
	Query         = queryBinding{}
	FormPost      = formPostBinding{}
	FormMultipart = formMultipartBinding{}
	ProtoBuf      = protobufBinding{}
	MsgPack       = msgpackBinding{}
)

// Default returns the appropriate Binding instance based on the HTTP method
// and the content type.
func Default(method, contentType string) Binding {
	if method == "GET" {
		return Form
	}

	switch contentType {
	case MIMEJSON:
		return JSON
	case MIMEXML, MIMEXML2:
		return XML
	case MIMEPROTOBUF:
		return ProtoBuf
	case MIMEMSGPACK, MIMEMSGPACK2:
		return MsgPack
	default: //case MIMEPOSTForm, MIMEMultipartPOSTForm:
		return Form
	}
}

func validate(obj interface{}) error {
	if Validator == nil {
		return nil
	}
	return Validator.ValidateStruct(obj)
}

<|edits_diff|>
--- binding/binding.go
+++ binding/binding.go
@@ -18,6 +18,7 @@
 	MIMEPROTOBUF          = "application/x-protobuf"
 	MIMEMSGPACK           = "application/x-msgpack"
 	MIMEMSGPACK2          = "application/msgpack"
+	MIMEYAML              = "application/x-yaml"
 )

 // Binding describes the interface which needs to be implemented for binding the
@@ -68,6 +69,7 @@
 	FormMultipart = formMultipartBinding{}
 	ProtoBuf      = protobufBinding{}
 	MsgPack       = msgpackBinding{}
+	YAML          = yamlBinding{}
 )

 // Default returns the appropriate Binding instance based on the HTTP method
<|current_version|>
// Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package binding

import "net/http"

// Content-Type MIME of the most common data formats.
const (
	MIMEJSON              = "application/json"
	MIMEHTML              = "text/html"
	MIMEXML               = "application/xml"
	MIMEXML2              = "text/xml"
	MIMEPlain             = "text/plain"
	MIMEPOSTForm          = "application/x-www-form-urlencoded"
	MIMEMultipartPOSTForm = "multipart/form-data"
	MIMEPROTOBUF          = "application/x-protobuf"
	MIMEMSGPACK           = "application/x-msgpack"
	MIMEMSGPACK2          = "application/msgpack"
	MIMEYAML              = "application/x-yaml"
)

// Binding describes the interface which needs to be implemented for binding the
// data present in the request such as JSON request body, query parameters or
// the form POST.
type Binding interface {
	Name() string
	Bind(*http.Request, interface{}) error
}

// BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
// but it reads the body from supplied bytes instead of req.Body.
type BindingBody interface {
	Binding
	BindBody([]byte, interface{}) error
}

// StructValidator is the minimal interface which needs to be implemented in
// order for it to be used as the validator engine for ensuring the correctness
// of the request. Gin provides a default implementation for this using
// https://github.com/go-playground/validator/tree/v8.18.2.
type StructValidator interface {
	// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
	// If the received type is not a struct, any validation should be skipped and nil must be returned.
	// If the received type is a struct or pointer to a struct, the validation should be performed.
	// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
	// Otherwise nil must be returned.
	ValidateStruct(interface{}) error

	// Engine returns the underlying validator engine which powers the
	// StructValidator implementation.
	Engine() interface{}
}

// Validator is the default validator which implements the StructValidator
// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2
// under the hood.
var Validator StructValidator = &defaultValidator{}

// These implement the Binding interface and can be used to bind the data
// present in the request to struct instances.
var (
	JSON          = jsonBinding{}
	XML           = xmlBinding{}
	Form          = formBinding{}
	Query         = queryBinding{}
	FormPost      = formPostBinding{}
	FormMultipart = formMultipartBinding{}
	ProtoBuf      = protobufBinding{}
	MsgPack       = msgpackBinding{}
	YAML          = yamlBinding{}
)

// Default returns the appropriate Binding instance based on the HTTP method
// and the content type.
func Default(method, contentType string) Binding {
	if method == "GET" {
		return Form
	}

	switch contentType {
	case MIMEJSON:
		return JSON
	case MIMEXML, MIMEXML2:
		return XML
	case MIMEPROTOBUF:
		return ProtoBuf
	case MIMEMSGPACK, MIMEMSGPACK2:
		return MsgPack
	default: //case MIMEPOSTForm, MIMEMultipartPOSTForm:
		return Form
	}
}

func validate(obj interface{}) error {
	if Validator == nil {
		return nil
	}
	return Validator.ValidateStruct(obj)
}

<|next_version|>
// Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package binding

import "net/http"

// Content-Type MIME of the most common data formats.
const (
	MIMEJSON              = "application/json"
	MIMEHTML              = "text/html"
	MIMEXML               = "application/xml"
	MIMEXML2              = "text/xml"
	MIMEPlain             = "text/plain"
	MIMEPOSTForm          = "application/x-www-form-urlencoded"
	MIMEMultipartPOSTForm = "multipart/form-data"
	MIMEPROTOBUF          = "application/x-protobuf"
	MIMEMSGPACK           = "application/x-msgpack"
	MIMEMSGPACK2          = "application/msgpack"
	MIMEYAML              = "application/x-yaml"
)

// Binding describes the interface which needs to be implemented for binding the
// data present in the request such as JSON request body, query parameters or
// the form POST.
type Binding interface {
	Name() string
	Bind(*http.Request, interface{}) error
}

// BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
// but it reads the body from supplied bytes instead of req.Body.
type BindingBody interface {
	Binding
	BindBody([]byte, interface{}) error
}

// StructValidator is the minimal interface which needs to be implemented in
// order for it to be used as the validator engine for ensuring the correctness
// of the request. Gin provides a default implementation for this using
// https://github.com/go-playground/validator/tree/v8.18.2.
type StructValidator interface {
	// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
	// If the received type is not a struct, any validation should be skipped and nil must be returned.
	// If the received type is a struct or pointer to a struct, the validation should be performed.
	// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
	// Otherwise nil must be returned.
	ValidateStruct(interface{}) error

	// Engine returns the underlying validator engine which powers the
	// StructValidator implementation.
	Engine() interface{}
}

// Validator is the default validator which implements the StructValidator
// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2
// under the hood.
var Validator StructValidator = &defaultValidator{}

// These implement the Binding interface and can be used to bind the data
// present in the request to struct instances.
var (
	JSON          = jsonBinding{}
	XML           = xmlBinding{}
	Form          = formBinding{}
	Query         = queryBinding{}
	FormPost      = formPostBinding{}
	FormMultipart = formMultipartBinding{}
	ProtoBuf      = protobufBinding{}
	MsgPack       = msgpackBinding{}
	YAML          = yamlBinding{}
)

// Default returns the appropriate Binding instance based on the HTTP method
// and the content type.
func Default(method, contentType string) Binding {
	if method == "GET" {
		return Form
	}

	switch contentType {
	case MIMEJSON:
		return JSON
	case MIMEXML, MIMEXML2:
		return XML
	case MIMEPROTOBUF:
		return ProtoBuf
	case MIMEMSGPACK, MIMEMSGPACK2:
		return MsgPack
	case MIMEYAML:
		return YAML
	default: //case MIMEPOSTForm, MIMEMultipartPOSTForm:
		return Form
	}
}

func validate(obj interface{}) error {
	if Validator == nil {
		return nil
	}
	return Validator.ValidateStruct(obj)
}


Updates realtime-advanced

<|original_code|>
package main

import (
	"html"
	"io"
	"time"

	"github.com/gin-gonic/gin"
)

func index(c *gin.Context) {
	c.Redirect(301, "/room/hn")
}

func roomGET(c *gin.Context) {
	roomid := c.ParamValue("roomid")
	nick := c.FormValue("nick")
	if len(nick) < 2 {
		nick = ""
	}
	if len(nick) > 13 {
		nick = nick[0:12] + "..."
	}
	c.HTML(200, "room_login.templ.html", gin.H{
		"roomid":    roomid,
		"nick":      nick,
		"timestamp": time.Now().Unix(),
	})

}

func roomPOST(c *gin.Context) {
	roomid := c.ParamValue("roomid")
	nick := c.FormValue("nick")
	message := c.PostFormValue("message")

	validMessage := len(message) > 1 && len(message) < 200
	validNick := len(nick) > 1 && len(nick) < 14
	if !validMessage || !validNick {
		c.JSON(400, gin.H{
			"status": "failed",
			"error":  "the message or nickname is too long",
		})
		return
	}

	post := gin.H{
		"nick":    html.EscapeString(nick),
		"message": html.EscapeString(message),
	}
	messages.Add("inbound", 1)
	room(roomid).Submit(post)
	c.JSON(200, post)
}

func streamRoom(c *gin.Context) {
	roomid := c.ParamValue("roomid")
	listener := openListener(roomid)
	ticker := time.NewTicker(1 * time.Second)
<|edits_diff|>
--- examples/realtime-advanced/routes.go
+++ examples/realtime-advanced/routes.go
@@ -3,6 +3,7 @@
 import (
 	"html"
 	"io"
+	"strings"
 	"time"

 	"github.com/gin-gonic/gin"
<|current_version|>
package main

import (
	"html"
	"io"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

func index(c *gin.Context) {
	c.Redirect(301, "/room/hn")
}

func roomGET(c *gin.Context) {
	roomid := c.ParamValue("roomid")
	nick := c.FormValue("nick")
	if len(nick) < 2 {
		nick = ""
	}
	if len(nick) > 13 {
		nick = nick[0:12] + "..."
	}
	c.HTML(200, "room_login.templ.html", gin.H{
		"roomid":    roomid,
		"nick":      nick,
		"timestamp": time.Now().Unix(),
	})

}

func roomPOST(c *gin.Context) {
	roomid := c.ParamValue("roomid")
	nick := c.FormValue("nick")
	message := c.PostFormValue("message")

	validMessage := len(message) > 1 && len(message) < 200
	validNick := len(nick) > 1 && len(nick) < 14
	if !validMessage || !validNick {
		c.JSON(400, gin.H{
			"status": "failed",
			"error":  "the message or nickname is too long",
		})
		return
	}

	post := gin.H{
		"nick":    html.EscapeString(nick),
		"message": html.EscapeString(message),
	}
	messages.Add("inbound", 1)
	room(roomid).Submit(post)
	c.JSON(200, post)
}

func streamRoom(c *gin.Context) {
	roomid := c.ParamValue("roomid")
	listener := openListener(roomid)
	ticker := time.NewTicker(1 * time.Second)
<|next_version|>
package main

import (
	"html"
	"io"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

func index(c *gin.Context) {
	c.Redirect(301, "/room/hn")
}

func roomGET(c *gin.Context) {
	roomid := c.ParamValue("roomid")
	nick := c.FormValue("nick")
	if len(nick) < 2 {
		nick = ""
	}
	if len(nick) > 13 {
		nick = nick[0:12] + "..."
	}
	c.HTML(200, "room_login.templ.html", gin.H{
		"roomid":    roomid,
		"nick":      nick,
		"timestamp": time.Now().Unix(),
	})

}

func roomPOST(c *gin.Context) {
	roomid := c.ParamValue("roomid")
	nick := c.FormValue("nick")
	message := c.PostFormValue("message")
	message = strings.TrimSpace(message)

	validMessage := len(message) > 1 && len(message) < 200
	validNick := len(nick) > 1 && len(nick) < 14
	if !validMessage || !validNick {
		c.JSON(400, gin.H{
			"status": "failed",
			"error":  "the message or nickname is too long",
		})
		return
	}

	post := gin.H{
		"nick":    html.EscapeString(nick),
		"message": html.EscapeString(message),
	}
	messages.Add("inbound", 1)
	room(roomid).Submit(post)
	c.JSON(200, post)
}

func streamRoom(c *gin.Context) {
	roomid := c.ParamValue("roomid")
	listener := openListener(roomid)
	ticker := time.NewTicker(1 * time.Second)


Updates tree.go + fixes + unit tests

<|original_code|>
		}
	}
	context.handlers = engine.allNoRoute
	serveError(context, 404, default404Body)
}

func (engine *Engine) serveAutoRedirect(c *Context, root *node, tsr bool) bool {
	req := c.Request
	path := req.URL.Path
	code := 301 // Permanent redirect, request with GET method
	if req.Method != "GET" {
		code = 307
	}

	if tsr && engine.RedirectTrailingSlash {
		if len(path) > 1 && path[len(path)-1] == '/' {
			req.URL.Path = path[:len(path)-1]
		} else {
			req.URL.Path = path + "/"
		}
		debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
		http.Redirect(c.Writer, req, req.URL.String(), code)
		return true
	}

	// Try to fix the request path
	if engine.RedirectFixedPath {
		fixedPath, found := root.findCaseInsensitivePath(
			CleanPath(path),
			engine.RedirectTrailingSlash,
		)
		if found {
			req.URL.Path = string(fixedPath)
			debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
			http.Redirect(c.Writer, req, req.URL.String(), code)
			return true
		}
	}
	return false
}

func serveError(c *Context, code int, defaultMessage []byte) {
	c.writermem.status = code
	c.Next()
	if !c.Writer.Written() {
		if c.Writer.Status() == code {
			c.Data(-1, binding.MIMEPlain, defaultMessage)
		} else {
			c.Writer.WriteHeaderNow()
		}
	}
}

<|edits_diff|>
--- gin.go
+++ gin.go
@@ -20,6 +20,7 @@
 		}
 		debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
 		http.Redirect(c.Writer, req, req.URL.String(), code)
+		c.writermem.WriteHeaderNow()
 		return true
 	}

<|current_version|>
		}
	}
	context.handlers = engine.allNoRoute
	serveError(context, 404, default404Body)
}

func (engine *Engine) serveAutoRedirect(c *Context, root *node, tsr bool) bool {
	req := c.Request
	path := req.URL.Path
	code := 301 // Permanent redirect, request with GET method
	if req.Method != "GET" {
		code = 307
	}

	if tsr && engine.RedirectTrailingSlash {
		if len(path) > 1 && path[len(path)-1] == '/' {
			req.URL.Path = path[:len(path)-1]
		} else {
			req.URL.Path = path + "/"
		}
		debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
		http.Redirect(c.Writer, req, req.URL.String(), code)
		c.writermem.WriteHeaderNow()
		return true
	}

	// Try to fix the request path
	if engine.RedirectFixedPath {
		fixedPath, found := root.findCaseInsensitivePath(
			CleanPath(path),
			engine.RedirectTrailingSlash,
		)
		if found {
			req.URL.Path = string(fixedPath)
			debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
			http.Redirect(c.Writer, req, req.URL.String(), code)
			return true
		}
	}
	return false
}

func serveError(c *Context, code int, defaultMessage []byte) {
	c.writermem.status = code
	c.Next()
	if !c.Writer.Written() {
		if c.Writer.Status() == code {
			c.Data(-1, binding.MIMEPlain, defaultMessage)
		} else {
			c.Writer.WriteHeaderNow()
		}
	}
}

<|next_version|>
		}
	}
	context.handlers = engine.allNoRoute
	serveError(context, 404, default404Body)
}

func (engine *Engine) serveAutoRedirect(c *Context, root *node, tsr bool) bool {
	req := c.Request
	path := req.URL.Path
	code := 301 // Permanent redirect, request with GET method
	if req.Method != "GET" {
		code = 307
	}

	if tsr && engine.RedirectTrailingSlash {
		if len(path) > 1 && path[len(path)-1] == '/' {
			req.URL.Path = path[:len(path)-1]
		} else {
			req.URL.Path = path + "/"
		}
		debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
		http.Redirect(c.Writer, req, req.URL.String(), code)
		c.writermem.WriteHeaderNow()
		return true
	}

	// Try to fix the request path
	if engine.RedirectFixedPath {
		fixedPath, found := root.findCaseInsensitivePath(
			CleanPath(path),
			engine.RedirectTrailingSlash,
		)
		if found {
			req.URL.Path = string(fixedPath)
			debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
			http.Redirect(c.Writer, req, req.URL.String(), code)
			c.writermem.WriteHeaderNow()
			return true
		}
	}
	return false
}

func serveError(c *Context, code int, defaultMessage []byte) {
	c.writermem.status = code
	c.Next()
	if !c.Writer.Written() {
		if c.Writer.Status() == code {
			c.Data(-1, binding.MIMEPlain, defaultMessage)
		} else {
			c.Writer.WriteHeaderNow()
		}
	}
}


Fix for #119. gin.LoadHTML* incorrectly works in debug mode.

<|original_code|>
func New() *Engine {
	engine := &Engine{}
	engine.RouterGroup = &RouterGroup{nil, "/", nil, engine}
	engine.router = httprouter.New()
	engine.router.NotFound = engine.handle404
	engine.cache.New = func() interface{} {
		c := &Context{Engine: engine}
		c.Writer = &c.writermem
		return c
	}
	return engine
}

// Returns a Engine instance with the Logger and Recovery already attached.
func Default() *Engine {
	engine := New()
	engine.Use(Recovery(), Logger())
	return engine
}

func (engine *Engine) LoadHTMLGlob(pattern string) {
	if gin_mode == debugCode {
		engine.HTMLRender = render.HTMLDebug
	} else {
		templ := template.Must(template.ParseGlob(pattern))
		engine.SetHTMLTemplate(templ)
	}
}

func (engine *Engine) LoadHTMLFiles(files ...string) {
	if gin_mode == debugCode {
		engine.HTMLRender = render.HTMLDebug
	} else {
		templ := template.Must(template.ParseFiles(files...))
		engine.SetHTMLTemplate(templ)
	}
}

func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
	engine.HTMLRender = render.HTMLRender{
		Template: templ,
	}
}

// Adds handlers for NoRoute. It return a 404 code by default.
func (engine *Engine) NoRoute(handlers ...HandlerFunc) {
	engine.noRoute = handlers
	engine.finalNoRoute = engine.combineHandlers(engine.noRoute)
}

func (engine *Engine) Use(middlewares ...HandlerFunc) {
	engine.RouterGroup.Use(middlewares...)
	engine.finalNoRoute = engine.combineHandlers(engine.noRoute)
}

<|edits_diff|>
--- gin.go
+++ gin.go
@@ -20,6 +20,7 @@

 func (engine *Engine) LoadHTMLGlob(pattern string) {
 	if gin_mode == debugCode {
+		render.HTMLDebug.AddGlob(pattern)
 		engine.HTMLRender = render.HTMLDebug
 	} else {
 		templ := template.Must(template.ParseGlob(pattern))
<|current_version|>
func New() *Engine {
	engine := &Engine{}
	engine.RouterGroup = &RouterGroup{nil, "/", nil, engine}
	engine.router = httprouter.New()
	engine.router.NotFound = engine.handle404
	engine.cache.New = func() interface{} {
		c := &Context{Engine: engine}
		c.Writer = &c.writermem
		return c
	}
	return engine
}

// Returns a Engine instance with the Logger and Recovery already attached.
func Default() *Engine {
	engine := New()
	engine.Use(Recovery(), Logger())
	return engine
}

func (engine *Engine) LoadHTMLGlob(pattern string) {
	if gin_mode == debugCode {
		render.HTMLDebug.AddGlob(pattern)
		engine.HTMLRender = render.HTMLDebug
	} else {
		templ := template.Must(template.ParseGlob(pattern))
		engine.SetHTMLTemplate(templ)
	}
}

func (engine *Engine) LoadHTMLFiles(files ...string) {
	if gin_mode == debugCode {
		engine.HTMLRender = render.HTMLDebug
	} else {
		templ := template.Must(template.ParseFiles(files...))
		engine.SetHTMLTemplate(templ)
	}
}

func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
	engine.HTMLRender = render.HTMLRender{
		Template: templ,
	}
}

// Adds handlers for NoRoute. It return a 404 code by default.
func (engine *Engine) NoRoute(handlers ...HandlerFunc) {
	engine.noRoute = handlers
	engine.finalNoRoute = engine.combineHandlers(engine.noRoute)
}

func (engine *Engine) Use(middlewares ...HandlerFunc) {
	engine.RouterGroup.Use(middlewares...)
	engine.finalNoRoute = engine.combineHandlers(engine.noRoute)
}

<|next_version|>
func New() *Engine {
	engine := &Engine{}
	engine.RouterGroup = &RouterGroup{nil, "/", nil, engine}
	engine.router = httprouter.New()
	engine.router.NotFound = engine.handle404
	engine.cache.New = func() interface{} {
		c := &Context{Engine: engine}
		c.Writer = &c.writermem
		return c
	}
	return engine
}

// Returns a Engine instance with the Logger and Recovery already attached.
func Default() *Engine {
	engine := New()
	engine.Use(Recovery(), Logger())
	return engine
}

func (engine *Engine) LoadHTMLGlob(pattern string) {
	if gin_mode == debugCode {
		render.HTMLDebug.AddGlob(pattern)
		engine.HTMLRender = render.HTMLDebug
	} else {
		templ := template.Must(template.ParseGlob(pattern))
		engine.SetHTMLTemplate(templ)
	}
}

func (engine *Engine) LoadHTMLFiles(files ...string) {
	if gin_mode == debugCode {
		render.HTMLDebug.AddFiles(files...)
		engine.HTMLRender = render.HTMLDebug
	} else {
		templ := template.Must(template.ParseFiles(files...))
		engine.SetHTMLTemplate(templ)
	}
}

func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
	engine.HTMLRender = render.HTMLRender{
		Template: templ,
	}
}

// Adds handlers for NoRoute. It return a 404 code by default.
func (engine *Engine) NoRoute(handlers ...HandlerFunc) {
	engine.noRoute = handlers
	engine.finalNoRoute = engine.combineHandlers(engine.noRoute)
}

func (engine *Engine) Use(middlewares ...HandlerFunc) {
	engine.RouterGroup.Use(middlewares...)
	engine.finalNoRoute = engine.combineHandlers(engine.noRoute)
}


Adds TestMode

<|original_code|>
package gin

import (
	"os"
)

const GIN_MODE = "GIN_MODE"

const (
	DebugMode   string = "debug"
	ReleaseMode string = "release"
)
const (
	debugCode   = iota
	releaseCode = iota
)

var gin_mode int = debugCode

func SetMode(value string) {
	switch value {
	case DebugMode:
		gin_mode = debugCode
	case ReleaseMode:
		gin_mode = releaseCode
	default:
		panic("gin mode unknown, the allowed modes are: " + DebugMode + " and " + ReleaseMode)
	}
}

func init() {
	value := os.Getenv(GIN_MODE)
	if len(value) == 0 {
		SetMode(DebugMode)
	} else {
		SetMode(value)
	}
}

<|edits_diff|>
--- mode.go
+++ mode.go
@@ -9,10 +9,12 @@
 const (
 	DebugMode   string = "debug"
 	ReleaseMode string = "release"
+	TestMode    string = "test"
 )
 const (
 	debugCode   = iota
 	releaseCode = iota
+	testCode    = iota
 )

 var gin_mode int = debugCode
<|current_version|>
package gin

import (
	"os"
)

const GIN_MODE = "GIN_MODE"

const (
	DebugMode   string = "debug"
	ReleaseMode string = "release"
	TestMode    string = "test"
)
const (
	debugCode   = iota
	releaseCode = iota
	testCode    = iota
)

var gin_mode int = debugCode

func SetMode(value string) {
	switch value {
	case DebugMode:
		gin_mode = debugCode
	case ReleaseMode:
		gin_mode = releaseCode
	default:
		panic("gin mode unknown, the allowed modes are: " + DebugMode + " and " + ReleaseMode)
	}
}

func init() {
	value := os.Getenv(GIN_MODE)
	if len(value) == 0 {
		SetMode(DebugMode)
	} else {
		SetMode(value)
	}
}

<|next_version|>
package gin

import (
	"os"
)

const GIN_MODE = "GIN_MODE"

const (
	DebugMode   string = "debug"
	ReleaseMode string = "release"
	TestMode    string = "test"
)
const (
	debugCode   = iota
	releaseCode = iota
	testCode    = iota
)

var gin_mode int = debugCode

func SetMode(value string) {
	switch value {
	case DebugMode:
		gin_mode = debugCode
	case ReleaseMode:
		gin_mode = releaseCode
	case TestMode:
		gin_mode = testCode
	default:
		panic("gin mode unknown, the allowed modes are: " + DebugMode + " and " + ReleaseMode)
	}
}

func init() {
	value := os.Getenv(GIN_MODE)
	if len(value) == 0 {
		SetMode(DebugMode)
	} else {
		SetMode(value)
	}
}


chore: enable TLS client cache for HTTPS where appropriate (#9721)

<|original_code|>
		}
	}

	// The http.Client used for announcements. It needs to have our
	// certificate to prove our identity, and may or may not verify the server
	// certificate depending on the insecure setting.
	var dialContext func(ctx context.Context, network, addr string) (net.Conn, error)
	if registry != nil {
		dialContext = dialer.DialContextReusePortFunc(registry)
	} else {
		dialContext = dialer.DialContext
	}
	var announceClient httpClient = &contextClient{&http.Client{
		Timeout: requestTimeout,
		Transport: http2EnabledTransport(&http.Transport{
			DialContext:       dialContext,
			Proxy:             http.ProxyFromEnvironment,
			DisableKeepAlives: true, // announcements are few and far between, so don't keep the connection open
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: opts.insecure,
				Certificates:       []tls.Certificate{cert},
				MinVersion:         tls.VersionTLS12,
			},
		}),
	}}
	if opts.id != "" {
		announceClient = newIDCheckingHTTPClient(announceClient, devID)
	}

	// The http.Client used for queries. We don't need to present our
	// certificate here, so lets not include it. May be insecure if requested.
	var queryClient httpClient = &contextClient{&http.Client{
		Timeout: requestTimeout,
		Transport: http2EnabledTransport(&http.Transport{
			DialContext:     dialer.DialContext,
			Proxy:           http.ProxyFromEnvironment,
			IdleConnTimeout: time.Second,
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: opts.insecure,
				MinVersion:         tls.VersionTLS12,
			},
		}),
	}}
	if opts.id != "" {
		queryClient = newIDCheckingHTTPClient(queryClient, devID)
	}

	cl := &globalClient{
		server:         server,
		addrList:       addrList,
		announceClient: announceClient,
		queryClient:    queryClient,
		noAnnounce:     opts.noAnnounce,
		noLookup:       opts.noLookup,
		evLogger:       evLogger,
	}
	if !opts.noAnnounce {
		// If we are supposed to announce, it's an error until we've done so.
		cl.setError(errors.New("not announced"))
	}

	return cl, nil
}

<|edits_diff|>
--- lib/discover/global.go
+++ lib/discover/global.go
@@ -20,6 +20,7 @@
 				InsecureSkipVerify: opts.insecure,
 				Certificates:       []tls.Certificate{cert},
 				MinVersion:         tls.VersionTLS12,
+				ClientSessionCache: tls.NewLRUClientSessionCache(0),
 			},
 		}),
 	}}
<|current_version|>
		}
	}

	// The http.Client used for announcements. It needs to have our
	// certificate to prove our identity, and may or may not verify the server
	// certificate depending on the insecure setting.
	var dialContext func(ctx context.Context, network, addr string) (net.Conn, error)
	if registry != nil {
		dialContext = dialer.DialContextReusePortFunc(registry)
	} else {
		dialContext = dialer.DialContext
	}
	var announceClient httpClient = &contextClient{&http.Client{
		Timeout: requestTimeout,
		Transport: http2EnabledTransport(&http.Transport{
			DialContext:       dialContext,
			Proxy:             http.ProxyFromEnvironment,
			DisableKeepAlives: true, // announcements are few and far between, so don't keep the connection open
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: opts.insecure,
				Certificates:       []tls.Certificate{cert},
				MinVersion:         tls.VersionTLS12,
				ClientSessionCache: tls.NewLRUClientSessionCache(0),
			},
		}),
	}}
	if opts.id != "" {
		announceClient = newIDCheckingHTTPClient(announceClient, devID)
	}

	// The http.Client used for queries. We don't need to present our
	// certificate here, so lets not include it. May be insecure if requested.
	var queryClient httpClient = &contextClient{&http.Client{
		Timeout: requestTimeout,
		Transport: http2EnabledTransport(&http.Transport{
			DialContext:     dialer.DialContext,
			Proxy:           http.ProxyFromEnvironment,
			IdleConnTimeout: time.Second,
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: opts.insecure,
				MinVersion:         tls.VersionTLS12,
			},
		}),
	}}
	if opts.id != "" {
		queryClient = newIDCheckingHTTPClient(queryClient, devID)
	}

	cl := &globalClient{
		server:         server,
		addrList:       addrList,
		announceClient: announceClient,
		queryClient:    queryClient,
		noAnnounce:     opts.noAnnounce,
		noLookup:       opts.noLookup,
		evLogger:       evLogger,
	}
	if !opts.noAnnounce {
		// If we are supposed to announce, it's an error until we've done so.
		cl.setError(errors.New("not announced"))
	}

	return cl, nil
}

<|next_version|>
		}
	}

	// The http.Client used for announcements. It needs to have our
	// certificate to prove our identity, and may or may not verify the server
	// certificate depending on the insecure setting.
	var dialContext func(ctx context.Context, network, addr string) (net.Conn, error)
	if registry != nil {
		dialContext = dialer.DialContextReusePortFunc(registry)
	} else {
		dialContext = dialer.DialContext
	}
	var announceClient httpClient = &contextClient{&http.Client{
		Timeout: requestTimeout,
		Transport: http2EnabledTransport(&http.Transport{
			DialContext:       dialContext,
			Proxy:             http.ProxyFromEnvironment,
			DisableKeepAlives: true, // announcements are few and far between, so don't keep the connection open
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: opts.insecure,
				Certificates:       []tls.Certificate{cert},
				MinVersion:         tls.VersionTLS12,
				ClientSessionCache: tls.NewLRUClientSessionCache(0),
			},
		}),
	}}
	if opts.id != "" {
		announceClient = newIDCheckingHTTPClient(announceClient, devID)
	}

	// The http.Client used for queries. We don't need to present our
	// certificate here, so lets not include it. May be insecure if requested.
	var queryClient httpClient = &contextClient{&http.Client{
		Timeout: requestTimeout,
		Transport: http2EnabledTransport(&http.Transport{
			DialContext:     dialer.DialContext,
			Proxy:           http.ProxyFromEnvironment,
			IdleConnTimeout: time.Second,
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: opts.insecure,
				MinVersion:         tls.VersionTLS12,
				ClientSessionCache: tls.NewLRUClientSessionCache(0),
			},
		}),
	}}
	if opts.id != "" {
		queryClient = newIDCheckingHTTPClient(queryClient, devID)
	}

	cl := &globalClient{
		server:         server,
		addrList:       addrList,
		announceClient: announceClient,
		queryClient:    queryClient,
		noAnnounce:     opts.noAnnounce,
		noLookup:       opts.noLookup,
		evLogger:       evLogger,
	}
	if !opts.noAnnounce {
		// If we are supposed to announce, it's an error until we've done so.
		cl.setError(errors.New("not announced"))
	}

	return cl, nil
}


lib/fs: Add missing locks to fakeFile methods (fixes #9499) (#9603)

<|original_code|>

	if f.content != nil {
		if len(f.content) < int(off)+len(p) {
			newc := make([]byte, int(off)+len(p))
			copy(newc, f.content)
			f.content = newc
		}
		copy(f.content[int(off):], p)
	}

	f.rng = nil
	f.offset = off + int64(len(p))
	if f.offset > f.size {
		f.size = f.offset
	}
	return len(p), nil
}

func (f *fakeFile) Name() string {
	if f.presentedName != "" {
		return f.presentedName
	}
	return f.name
}

func (f *fakeFile) Truncate(size int64) error {
	f.mut.Lock()
	defer f.mut.Unlock()

	if f.content != nil {
		if int64(cap(f.content)) < size {
			c := make([]byte, size)
			copy(c[:len(f.content)], f.content)
			f.content = c
		} else {
			f.content = f.content[:int(size)]
		}
	}
	f.rng = nil
	f.size = size
	if f.offset > size {
		f.offset = size
	}
	return nil
}

func (f *fakeFile) Stat() (FileInfo, error) {
	info := &fakeFileInfo{*f.fakeEntry}
	if f.presentedName != "" {
		info.name = f.presentedName
	}

	return info, nil
}

func (*fakeFile) Sync() error {
	return nil
}

// fakeFileInfo is the stat result.
type fakeFileInfo struct {
	fakeEntry // intentionally a copy of the struct
}

func (f *fakeFileInfo) Name() string {
	return f.name
}

func (f *fakeFileInfo) Mode() FileMode {
	return f.mode
}

<|edits_diff|>
--- lib/fs/fakefs.go
+++ lib/fs/fakefs.go
@@ -20,6 +20,8 @@
 	if f.presentedName != "" {
 		return f.presentedName
 	}
+	f.mut.Lock()
+	defer f.mut.Unlock()
 	return f.name
 }

@@ -45,6 +47,7 @@
 }

 func (f *fakeFile) Stat() (FileInfo, error) {
+	f.mut.Lock()
 	info := &fakeFileInfo{*f.fakeEntry}
 	if f.presentedName != "" {
 		info.name = f.presentedName
<|current_version|>

	if f.content != nil {
		if len(f.content) < int(off)+len(p) {
			newc := make([]byte, int(off)+len(p))
			copy(newc, f.content)
			f.content = newc
		}
		copy(f.content[int(off):], p)
	}

	f.rng = nil
	f.offset = off + int64(len(p))
	if f.offset > f.size {
		f.size = f.offset
	}
	return len(p), nil
}

func (f *fakeFile) Name() string {
	if f.presentedName != "" {
		return f.presentedName
	}
	f.mut.Lock()
	defer f.mut.Unlock()
	return f.name
}

func (f *fakeFile) Truncate(size int64) error {
	f.mut.Lock()
	defer f.mut.Unlock()

	if f.content != nil {
		if int64(cap(f.content)) < size {
			c := make([]byte, size)
			copy(c[:len(f.content)], f.content)
			f.content = c
		} else {
			f.content = f.content[:int(size)]
		}
	}
	f.rng = nil
	f.size = size
	if f.offset > size {
		f.offset = size
	}
	return nil
}

func (f *fakeFile) Stat() (FileInfo, error) {
	f.mut.Lock()
	info := &fakeFileInfo{*f.fakeEntry}
	if f.presentedName != "" {
		info.name = f.presentedName
	}

	return info, nil
}

func (*fakeFile) Sync() error {
	return nil
}

// fakeFileInfo is the stat result.
type fakeFileInfo struct {
	fakeEntry // intentionally a copy of the struct
}

func (f *fakeFileInfo) Name() string {
	return f.name
}

func (f *fakeFileInfo) Mode() FileMode {
	return f.mode
}

<|next_version|>

	if f.content != nil {
		if len(f.content) < int(off)+len(p) {
			newc := make([]byte, int(off)+len(p))
			copy(newc, f.content)
			f.content = newc
		}
		copy(f.content[int(off):], p)
	}

	f.rng = nil
	f.offset = off + int64(len(p))
	if f.offset > f.size {
		f.size = f.offset
	}
	return len(p), nil
}

func (f *fakeFile) Name() string {
	if f.presentedName != "" {
		return f.presentedName
	}
	f.mut.Lock()
	defer f.mut.Unlock()
	return f.name
}

func (f *fakeFile) Truncate(size int64) error {
	f.mut.Lock()
	defer f.mut.Unlock()

	if f.content != nil {
		if int64(cap(f.content)) < size {
			c := make([]byte, size)
			copy(c[:len(f.content)], f.content)
			f.content = c
		} else {
			f.content = f.content[:int(size)]
		}
	}
	f.rng = nil
	f.size = size
	if f.offset > size {
		f.offset = size
	}
	return nil
}

func (f *fakeFile) Stat() (FileInfo, error) {
	f.mut.Lock()
	info := &fakeFileInfo{*f.fakeEntry}
	f.mut.Unlock()
	if f.presentedName != "" {
		info.name = f.presentedName
	}

	return info, nil
}

func (*fakeFile) Sync() error {
	return nil
}

// fakeFileInfo is the stat result.
type fakeFileInfo struct {
	fakeEntry // intentionally a copy of the struct
}

func (f *fakeFileInfo) Name() string {
	return f.name
}

func (f *fakeFileInfo) Mode() FileMode {
	return f.mode
}


cmd/stcrashreceiver: Aggregate slice out of bounds errors

<|original_code|>
		}
	}

	pkt := packet(version, "crash")
	pkt.Message = string(subjectLine)
	pkt.Extra = raven.Extra{
		"url": reportServer + path,
	}
	pkt.Interfaces = []raven.Interface{&trace}
	pkt.Fingerprint = crashReportFingerprint(pkt.Message)

	return pkt, nil
}

var (
	indexRe          = regexp.MustCompile(`\[[-:0-9]+\]`)
	sizeRe           = regexp.MustCompile(`(length|capacity) [0-9]+`)
	ldbPosRe         = regexp.MustCompile(`(\(pos=)([0-9]+)\)`)
	ldbChecksumRe    = regexp.MustCompile(`(want=0x)([a-z0-9]+)( got=0x)([a-z0-9]+)`)
	ldbFileRe        = regexp.MustCompile(`(\[file=)([0-9]+)(\.ldb\])`)
	ldbInternalKeyRe = regexp.MustCompile(`(internal key ")[^"]+(", len=)[0-9]+`)
	ldbPathRe        = regexp.MustCompile(`(open|write|read) .+[\\/].+[\\/]index[^\\/]+[\\/][^\\/]+: `)
)

func sanitizeMessageLDB(message string) string {
	message = ldbPosRe.ReplaceAllString(message, "${1}x)")
	message = ldbFileRe.ReplaceAllString(message, "${1}x${3}")
	message = ldbChecksumRe.ReplaceAllString(message, "${1}X${3}X")
	message = ldbInternalKeyRe.ReplaceAllString(message, "${1}x${2}x")
	message = ldbPathRe.ReplaceAllString(message, "$1 x: ")
	return message
}

func crashReportFingerprint(message string) []string {
	// Do not fingerprint on the stack in case of db corruption or fatal
	// db io error - where it occurs doesn't matter.
	orig := message
	message = sanitizeMessageLDB(message)
	if message != orig {
		return []string{message}
	}

	message = indexRe.ReplaceAllString(message, "[x]")
	message = sizeRe.ReplaceAllString(message, "$1 x")

	// {{ default }} is what sentry uses as a fingerprint by default. While
	// never specified, the docs point at this being some hash derived from the
	// stack trace. Here we include the filtered panic message on top of that.
	// https://docs.sentry.io/platforms/go/data-management/event-grouping/sdk-fingerprinting/#basic-example
	return []string{"{{ default }}", message}
}

// syncthing v1.1.4-rc.1+30-g6aaae618-dirty-crashrep "Erbium Earthworm" (go1.12.5 darwin-amd64) jb@kvin.kastelo.net 2019-05-23 16:08:14 UTC [foo, bar]
// or, somewhere along the way the "+" in the version tag disappeared:
<|edits_diff|>
--- cmd/stcrashreceiver/sentry.go
+++ cmd/stcrashreceiver/sentry.go
@@ -20,6 +20,7 @@
 	ldbFileRe        = regexp.MustCompile(`(\[file=)([0-9]+)(\.ldb\])`)
 	ldbInternalKeyRe = regexp.MustCompile(`(internal key ")[^"]+(", len=)[0-9]+`)
 	ldbPathRe        = regexp.MustCompile(`(open|write|read) .+[\\/].+[\\/]index[^\\/]+[\\/][^\\/]+: `)
+	sliceBoundsRe    = regexp.MustCompile(`(slice bounds out of range) \[.+`)
 )

 func sanitizeMessageLDB(message string) string {
<|current_version|>
		}
	}

	pkt := packet(version, "crash")
	pkt.Message = string(subjectLine)
	pkt.Extra = raven.Extra{
		"url": reportServer + path,
	}
	pkt.Interfaces = []raven.Interface{&trace}
	pkt.Fingerprint = crashReportFingerprint(pkt.Message)

	return pkt, nil
}

var (
	indexRe          = regexp.MustCompile(`\[[-:0-9]+\]`)
	sizeRe           = regexp.MustCompile(`(length|capacity) [0-9]+`)
	ldbPosRe         = regexp.MustCompile(`(\(pos=)([0-9]+)\)`)
	ldbChecksumRe    = regexp.MustCompile(`(want=0x)([a-z0-9]+)( got=0x)([a-z0-9]+)`)
	ldbFileRe        = regexp.MustCompile(`(\[file=)([0-9]+)(\.ldb\])`)
	ldbInternalKeyRe = regexp.MustCompile(`(internal key ")[^"]+(", len=)[0-9]+`)
	ldbPathRe        = regexp.MustCompile(`(open|write|read) .+[\\/].+[\\/]index[^\\/]+[\\/][^\\/]+: `)
	sliceBoundsRe    = regexp.MustCompile(`(slice bounds out of range) \[.+`)
)

func sanitizeMessageLDB(message string) string {
	message = ldbPosRe.ReplaceAllString(message, "${1}x)")
	message = ldbFileRe.ReplaceAllString(message, "${1}x${3}")
	message = ldbChecksumRe.ReplaceAllString(message, "${1}X${3}X")
	message = ldbInternalKeyRe.ReplaceAllString(message, "${1}x${2}x")
	message = ldbPathRe.ReplaceAllString(message, "$1 x: ")
	return message
}

func crashReportFingerprint(message string) []string {
	// Do not fingerprint on the stack in case of db corruption or fatal
	// db io error - where it occurs doesn't matter.
	orig := message
	message = sanitizeMessageLDB(message)
	if message != orig {
		return []string{message}
	}

	message = indexRe.ReplaceAllString(message, "[x]")
	message = sizeRe.ReplaceAllString(message, "$1 x")

	// {{ default }} is what sentry uses as a fingerprint by default. While
	// never specified, the docs point at this being some hash derived from the
	// stack trace. Here we include the filtered panic message on top of that.
	// https://docs.sentry.io/platforms/go/data-management/event-grouping/sdk-fingerprinting/#basic-example
	return []string{"{{ default }}", message}
}

// syncthing v1.1.4-rc.1+30-g6aaae618-dirty-crashrep "Erbium Earthworm" (go1.12.5 darwin-amd64) jb@kvin.kastelo.net 2019-05-23 16:08:14 UTC [foo, bar]
// or, somewhere along the way the "+" in the version tag disappeared:
<|next_version|>
		}
	}

	pkt := packet(version, "crash")
	pkt.Message = string(subjectLine)
	pkt.Extra = raven.Extra{
		"url": reportServer + path,
	}
	pkt.Interfaces = []raven.Interface{&trace}
	pkt.Fingerprint = crashReportFingerprint(pkt.Message)

	return pkt, nil
}

var (
	indexRe          = regexp.MustCompile(`\[[-:0-9]+\]`)
	sizeRe           = regexp.MustCompile(`(length|capacity) [0-9]+`)
	ldbPosRe         = regexp.MustCompile(`(\(pos=)([0-9]+)\)`)
	ldbChecksumRe    = regexp.MustCompile(`(want=0x)([a-z0-9]+)( got=0x)([a-z0-9]+)`)
	ldbFileRe        = regexp.MustCompile(`(\[file=)([0-9]+)(\.ldb\])`)
	ldbInternalKeyRe = regexp.MustCompile(`(internal key ")[^"]+(", len=)[0-9]+`)
	ldbPathRe        = regexp.MustCompile(`(open|write|read) .+[\\/].+[\\/]index[^\\/]+[\\/][^\\/]+: `)
	sliceBoundsRe    = regexp.MustCompile(`(slice bounds out of range) \[.+`)
)

func sanitizeMessageLDB(message string) string {
	message = ldbPosRe.ReplaceAllString(message, "${1}x)")
	message = ldbFileRe.ReplaceAllString(message, "${1}x${3}")
	message = ldbChecksumRe.ReplaceAllString(message, "${1}X${3}X")
	message = ldbInternalKeyRe.ReplaceAllString(message, "${1}x${2}x")
	message = ldbPathRe.ReplaceAllString(message, "$1 x: ")
	message = sliceBoundsRe.ReplaceAllString(message, "$1")
	return message
}

func crashReportFingerprint(message string) []string {
	// Do not fingerprint on the stack in case of db corruption or fatal
	// db io error - where it occurs doesn't matter.
	orig := message
	message = sanitizeMessageLDB(message)
	if message != orig {
		return []string{message}
	}

	message = indexRe.ReplaceAllString(message, "[x]")
	message = sizeRe.ReplaceAllString(message, "$1 x")

	// {{ default }} is what sentry uses as a fingerprint by default. While
	// never specified, the docs point at this being some hash derived from the
	// stack trace. Here we include the filtered panic message on top of that.
	// https://docs.sentry.io/platforms/go/data-management/event-grouping/sdk-fingerprinting/#basic-example
	return []string{"{{ default }}", message}
}

// syncthing v1.1.4-rc.1+30-g6aaae618-dirty-crashrep "Erbium Earthworm" (go1.12.5 darwin-amd64) jb@kvin.kastelo.net 2019-05-23 16:08:14 UTC [foo, bar]
// or, somewhere along the way the "+" in the version tag disappeared:


all: Add Prometheus-style metrics to expose some internal performance counters (fixes #5175) (#9003)

<|original_code|>
func NewLogger() Logger {
	l := &logger{
		timeout:       time.NewTimer(time.Second),
		events:        make(chan Event, BufferSize),
		funcs:         make(chan func(context.Context)),
		toUnsubscribe: make(chan *subscription),
	}
	// Make sure the timer is in the stopped state and hasn't fired anything
	// into the channel.
	if !l.timeout.Stop() {
		<-l.timeout.C
	}
	return l
}

func (l *logger) Serve(ctx context.Context) error {
loop:
	for {
		select {
		case e := <-l.events:
			// Incoming events get sent
			l.sendEvent(e)

		case fn := <-l.funcs:
			// Subscriptions are handled here.
			fn(ctx)

		case s := <-l.toUnsubscribe:
			l.unsubscribe(s)

		case <-ctx.Done():
			break loop
		}
	}

	// Closing the event channels corresponds to what happens when a
	// subscription is unsubscribed; this stops any BufferedSubscription,
	// makes Poll() return ErrClosed, etc.
	for _, s := range l.subs {
		close(s.events)
	}

	return nil
}

func (l *logger) Log(t EventType, data interface{}) {
	l.events <- Event{
		Time: time.Now(), // intentionally high precision
		Type: t,
		Data: data,
		// SubscriptionID and GlobalID are set in sendEvent
	}
}

func (l *logger) sendEvent(e Event) {
	l.nextGlobalID++
	dl.Debugln("log", l.nextGlobalID, e.Type, e.Data)

	e.GlobalID = l.nextGlobalID

	for i, s := range l.subs {
		if s.mask&e.Type != 0 {
			e.SubscriptionID = l.nextSubscriptionIDs[i]
			l.nextSubscriptionIDs[i]++

			l.timeout.Reset(eventLogTimeout)
			timedOut := false

			select {
			case s.events <- e:
			case <-l.timeout.C:
				// if s.events is not ready, drop the event
				timedOut = true
			}

			// If stop returns false it already sent something to the
			// channel. If we didn't already read it above we must do so now
			// or we get a spurious timeout on the next loop.
			if !l.timeout.Stop() && !timedOut {
				<-l.timeout.C
			}
		}
	}
}

func (l *logger) Subscribe(mask EventType) Subscription {
	res := make(chan Subscription)
	l.funcs <- func(ctx context.Context) {
		dl.Debugln("subscribe", mask)

		s := &subscription{
			mask:          mask,
			events:        make(chan Event, BufferSize),
			toUnsubscribe: l.toUnsubscribe,
			timeout:       time.NewTimer(0),
			ctx:           ctx,
		}
<|edits_diff|>
--- lib/events/events.go
+++ lib/events/events.go
@@ -20,6 +20,7 @@
 		case e := <-l.events:
 			// Incoming events get sent
 			l.sendEvent(e)
+			metricEvents.WithLabelValues(e.Type.String(), metricEventStateCreated).Inc()

 		case fn := <-l.funcs:
 			// Subscriptions are handled here.
@@ -68,6 +69,7 @@

 			select {
 			case s.events <- e:
+				metricEvents.WithLabelValues(e.Type.String(), metricEventStateDelivered).Inc()
 			case <-l.timeout.C:
 				// if s.events is not ready, drop the event
 				timedOut = true
<|current_version|>
func NewLogger() Logger {
	l := &logger{
		timeout:       time.NewTimer(time.Second),
		events:        make(chan Event, BufferSize),
		funcs:         make(chan func(context.Context)),
		toUnsubscribe: make(chan *subscription),
	}
	// Make sure the timer is in the stopped state and hasn't fired anything
	// into the channel.
	if !l.timeout.Stop() {
		<-l.timeout.C
	}
	return l
}

func (l *logger) Serve(ctx context.Context) error {
loop:
	for {
		select {
		case e := <-l.events:
			// Incoming events get sent
			l.sendEvent(e)
			metricEvents.WithLabelValues(e.Type.String(), metricEventStateCreated).Inc()

		case fn := <-l.funcs:
			// Subscriptions are handled here.
			fn(ctx)

		case s := <-l.toUnsubscribe:
			l.unsubscribe(s)

		case <-ctx.Done():
			break loop
		}
	}

	// Closing the event channels corresponds to what happens when a
	// subscription is unsubscribed; this stops any BufferedSubscription,
	// makes Poll() return ErrClosed, etc.
	for _, s := range l.subs {
		close(s.events)
	}

	return nil
}

func (l *logger) Log(t EventType, data interface{}) {
	l.events <- Event{
		Time: time.Now(), // intentionally high precision
		Type: t,
		Data: data,
		// SubscriptionID and GlobalID are set in sendEvent
	}
}

func (l *logger) sendEvent(e Event) {
	l.nextGlobalID++
	dl.Debugln("log", l.nextGlobalID, e.Type, e.Data)

	e.GlobalID = l.nextGlobalID

	for i, s := range l.subs {
		if s.mask&e.Type != 0 {
			e.SubscriptionID = l.nextSubscriptionIDs[i]
			l.nextSubscriptionIDs[i]++

			l.timeout.Reset(eventLogTimeout)
			timedOut := false

			select {
			case s.events <- e:
				metricEvents.WithLabelValues(e.Type.String(), metricEventStateDelivered).Inc()
			case <-l.timeout.C:
				// if s.events is not ready, drop the event
				timedOut = true
			}

			// If stop returns false it already sent something to the
			// channel. If we didn't already read it above we must do so now
			// or we get a spurious timeout on the next loop.
			if !l.timeout.Stop() && !timedOut {
				<-l.timeout.C
			}
		}
	}
}

func (l *logger) Subscribe(mask EventType) Subscription {
	res := make(chan Subscription)
	l.funcs <- func(ctx context.Context) {
		dl.Debugln("subscribe", mask)

		s := &subscription{
			mask:          mask,
			events:        make(chan Event, BufferSize),
			toUnsubscribe: l.toUnsubscribe,
			timeout:       time.NewTimer(0),
			ctx:           ctx,
		}
<|next_version|>
func NewLogger() Logger {
	l := &logger{
		timeout:       time.NewTimer(time.Second),
		events:        make(chan Event, BufferSize),
		funcs:         make(chan func(context.Context)),
		toUnsubscribe: make(chan *subscription),
	}
	// Make sure the timer is in the stopped state and hasn't fired anything
	// into the channel.
	if !l.timeout.Stop() {
		<-l.timeout.C
	}
	return l
}

func (l *logger) Serve(ctx context.Context) error {
loop:
	for {
		select {
		case e := <-l.events:
			// Incoming events get sent
			l.sendEvent(e)
			metricEvents.WithLabelValues(e.Type.String(), metricEventStateCreated).Inc()

		case fn := <-l.funcs:
			// Subscriptions are handled here.
			fn(ctx)

		case s := <-l.toUnsubscribe:
			l.unsubscribe(s)

		case <-ctx.Done():
			break loop
		}
	}

	// Closing the event channels corresponds to what happens when a
	// subscription is unsubscribed; this stops any BufferedSubscription,
	// makes Poll() return ErrClosed, etc.
	for _, s := range l.subs {
		close(s.events)
	}

	return nil
}

func (l *logger) Log(t EventType, data interface{}) {
	l.events <- Event{
		Time: time.Now(), // intentionally high precision
		Type: t,
		Data: data,
		// SubscriptionID and GlobalID are set in sendEvent
	}
}

func (l *logger) sendEvent(e Event) {
	l.nextGlobalID++
	dl.Debugln("log", l.nextGlobalID, e.Type, e.Data)

	e.GlobalID = l.nextGlobalID

	for i, s := range l.subs {
		if s.mask&e.Type != 0 {
			e.SubscriptionID = l.nextSubscriptionIDs[i]
			l.nextSubscriptionIDs[i]++

			l.timeout.Reset(eventLogTimeout)
			timedOut := false

			select {
			case s.events <- e:
				metricEvents.WithLabelValues(e.Type.String(), metricEventStateDelivered).Inc()
			case <-l.timeout.C:
				// if s.events is not ready, drop the event
				timedOut = true
				metricEvents.WithLabelValues(e.Type.String(), metricEventStateDropped).Inc()
			}

			// If stop returns false it already sent something to the
			// channel. If we didn't already read it above we must do so now
			// or we get a spurious timeout on the next loop.
			if !l.timeout.Stop() && !timedOut {
				<-l.timeout.C
			}
		}
	}
}

func (l *logger) Subscribe(mask EventType) Subscription {
	res := make(chan Subscription)
	l.funcs <- func(ctx context.Context) {
		dl.Debugln("subscribe", mask)

		s := &subscription{
			mask:          mask,
			events:        make(chan Event, BufferSize),
			toUnsubscribe: l.toUnsubscribe,
			timeout:       time.NewTimer(0),
			ctx:           ctx,
		}


gui, lib/connections: Let the backend decide whether connection is local (fixes #8686) (#8694)

<|original_code|>
	"github.com/syncthing/syncthing/lib/nat"
	"github.com/syncthing/syncthing/lib/osutil"
	"github.com/syncthing/syncthing/lib/protocol"
	"github.com/syncthing/syncthing/lib/stats"

	"github.com/thejerf/suture/v4"
)

type tlsConn interface {
	io.ReadWriteCloser
	ConnectionState() tls.ConnectionState
	RemoteAddr() net.Addr
	SetDeadline(time.Time) error
	SetWriteDeadline(time.Time) error
	LocalAddr() net.Addr
}

// internalConn is the raw TLS connection plus some metadata on where it
// came from (type, priority).
type internalConn struct {
	tlsConn
	connType      connType
	priority      int
	establishedAt time.Time
}

type connType int

const (
	connTypeRelayClient connType = iota
	connTypeRelayServer
	connTypeTCPClient
	connTypeTCPServer
	connTypeQUICClient
	connTypeQUICServer
)

func (t connType) String() string {
	switch t {
	case connTypeRelayClient:
		return "relay-client"
	case connTypeRelayServer:
		return "relay-server"
	case connTypeTCPClient:
		return "tcp-client"
	case connTypeTCPServer:
		return "tcp-server"
	case connTypeQUICClient:
		return "quic-client"
	case connTypeQUICServer:
		return "quic-server"
	default:
		return "unknown-type"
	}
}

func (t connType) Transport() string {
	switch t {
	case connTypeRelayClient, connTypeRelayServer:
		return "relay"
	case connTypeTCPClient, connTypeTCPServer:
		return "tcp"
	case connTypeQUICClient, connTypeQUICServer:
		return "quic"
	default:
		return "unknown"
	}
}

func newInternalConn(tc tlsConn, connType connType, priority int) internalConn {
	return internalConn{
		tlsConn:       tc,
		connType:      connType,
		priority:      priority,
		establishedAt: time.Now().Truncate(time.Second),
	}
}

func (c internalConn) Close() error {
	// *tls.Conn.Close() does more than it says on the tin. Specifically, it
	// sends a TLS alert message, which might block forever if the
	// connection is dead and we don't have a deadline set.
	_ = c.SetWriteDeadline(time.Now().Add(250 * time.Millisecond))
	return c.tlsConn.Close()
}

func (c internalConn) Type() string {
	return c.connType.String()
}

func (c internalConn) Priority() int {
	return c.priority
}

func (c internalConn) Crypto() string {
	cs := c.ConnectionState()
	return fmt.Sprintf("%s-%s", tlsVersionNames[cs.Version], tlsCipherSuiteNames[cs.CipherSuite])
}

func (c internalConn) Transport() string {
	transport := c.connType.Transport()
	ip, err := osutil.IPFromAddr(c.LocalAddr())
	if err != nil {
		return transport
	}
	if ip.To4() != nil {
		return transport + "4"
	}
	return transport + "6"
}

func (c internalConn) EstablishedAt() time.Time {
<|edits_diff|>
--- lib/connections/structs.go
+++ lib/connections/structs.go
@@ -20,6 +20,7 @@
 type internalConn struct {
 	tlsConn
 	connType      connType
+	isLocal       bool
 	priority      int
 	establishedAt time.Time
 }
<|current_version|>
	"github.com/syncthing/syncthing/lib/nat"
	"github.com/syncthing/syncthing/lib/osutil"
	"github.com/syncthing/syncthing/lib/protocol"
	"github.com/syncthing/syncthing/lib/stats"

	"github.com/thejerf/suture/v4"
)

type tlsConn interface {
	io.ReadWriteCloser
	ConnectionState() tls.ConnectionState
	RemoteAddr() net.Addr
	SetDeadline(time.Time) error
	SetWriteDeadline(time.Time) error
	LocalAddr() net.Addr
}

// internalConn is the raw TLS connection plus some metadata on where it
// came from (type, priority).
type internalConn struct {
	tlsConn
	connType      connType
	isLocal       bool
	priority      int
	establishedAt time.Time
}

type connType int

const (
	connTypeRelayClient connType = iota
	connTypeRelayServer
	connTypeTCPClient
	connTypeTCPServer
	connTypeQUICClient
	connTypeQUICServer
)

func (t connType) String() string {
	switch t {
	case connTypeRelayClient:
		return "relay-client"
	case connTypeRelayServer:
		return "relay-server"
	case connTypeTCPClient:
		return "tcp-client"
	case connTypeTCPServer:
		return "tcp-server"
	case connTypeQUICClient:
		return "quic-client"
	case connTypeQUICServer:
		return "quic-server"
	default:
		return "unknown-type"
	}
}

func (t connType) Transport() string {
	switch t {
	case connTypeRelayClient, connTypeRelayServer:
		return "relay"
	case connTypeTCPClient, connTypeTCPServer:
		return "tcp"
	case connTypeQUICClient, connTypeQUICServer:
		return "quic"
	default:
		return "unknown"
	}
}

func newInternalConn(tc tlsConn, connType connType, priority int) internalConn {
	return internalConn{
		tlsConn:       tc,
		connType:      connType,
		priority:      priority,
		establishedAt: time.Now().Truncate(time.Second),
	}
}

func (c internalConn) Close() error {
	// *tls.Conn.Close() does more than it says on the tin. Specifically, it
	// sends a TLS alert message, which might block forever if the
	// connection is dead and we don't have a deadline set.
	_ = c.SetWriteDeadline(time.Now().Add(250 * time.Millisecond))
	return c.tlsConn.Close()
}

func (c internalConn) Type() string {
	return c.connType.String()
}

func (c internalConn) Priority() int {
	return c.priority
}

func (c internalConn) Crypto() string {
	cs := c.ConnectionState()
	return fmt.Sprintf("%s-%s", tlsVersionNames[cs.Version], tlsCipherSuiteNames[cs.CipherSuite])
}

func (c internalConn) Transport() string {
	transport := c.connType.Transport()
	ip, err := osutil.IPFromAddr(c.LocalAddr())
	if err != nil {
		return transport
	}
	if ip.To4() != nil {
		return transport + "4"
	}
	return transport + "6"
}

func (c internalConn) EstablishedAt() time.Time {
<|next_version|>
	"github.com/syncthing/syncthing/lib/nat"
	"github.com/syncthing/syncthing/lib/osutil"
	"github.com/syncthing/syncthing/lib/protocol"
	"github.com/syncthing/syncthing/lib/stats"

	"github.com/thejerf/suture/v4"
)

type tlsConn interface {
	io.ReadWriteCloser
	ConnectionState() tls.ConnectionState
	RemoteAddr() net.Addr
	SetDeadline(time.Time) error
	SetWriteDeadline(time.Time) error
	LocalAddr() net.Addr
}

// internalConn is the raw TLS connection plus some metadata on where it
// came from (type, priority).
type internalConn struct {
	tlsConn
	connType      connType
	isLocal       bool
	priority      int
	establishedAt time.Time
}

type connType int

const (
	connTypeRelayClient connType = iota
	connTypeRelayServer
	connTypeTCPClient
	connTypeTCPServer
	connTypeQUICClient
	connTypeQUICServer
)

func (t connType) String() string {
	switch t {
	case connTypeRelayClient:
		return "relay-client"
	case connTypeRelayServer:
		return "relay-server"
	case connTypeTCPClient:
		return "tcp-client"
	case connTypeTCPServer:
		return "tcp-server"
	case connTypeQUICClient:
		return "quic-client"
	case connTypeQUICServer:
		return "quic-server"
	default:
		return "unknown-type"
	}
}

func (t connType) Transport() string {
	switch t {
	case connTypeRelayClient, connTypeRelayServer:
		return "relay"
	case connTypeTCPClient, connTypeTCPServer:
		return "tcp"
	case connTypeQUICClient, connTypeQUICServer:
		return "quic"
	default:
		return "unknown"
	}
}

func newInternalConn(tc tlsConn, connType connType, priority int) internalConn {
	return internalConn{
		tlsConn:       tc,
		connType:      connType,
		priority:      priority,
		establishedAt: time.Now().Truncate(time.Second),
	}
}

func (c internalConn) Close() error {
	// *tls.Conn.Close() does more than it says on the tin. Specifically, it
	// sends a TLS alert message, which might block forever if the
	// connection is dead and we don't have a deadline set.
	_ = c.SetWriteDeadline(time.Now().Add(250 * time.Millisecond))
	return c.tlsConn.Close()
}

func (c internalConn) Type() string {
	return c.connType.String()
}

func (c internalConn) IsLocal() bool {
	return c.isLocal
}

func (c internalConn) Priority() int {
	return c.priority
}

func (c internalConn) Crypto() string {
	cs := c.ConnectionState()
	return fmt.Sprintf("%s-%s", tlsVersionNames[cs.Version], tlsCipherSuiteNames[cs.CipherSuite])
}

func (c internalConn) Transport() string {
	transport := c.connType.Transport()
	ip, err := osutil.IPFromAddr(c.LocalAddr())
	if err != nil {
		return transport
	}
	if ip.To4() != nil {
		return transport + "4"
	}
	return transport + "6"
}

func (c internalConn) EstablishedAt() time.Time {


lib/model: Missing fmut-lock on encryption failures (#7841)

<|original_code|>
			m.evLogger.Log(events.FolderRejected, map[string]string{
				"folder":      folder.ID,
				"folderLabel": folder.Label,
				"device":      deviceID.String(),
			})
			l.Infof("Unexpected folder %s sent from device %q; ensure that the folder exists and that this device is selected under \"Share With\" in the folder configuration.", folder.Description(), deviceID)
			continue
		}

		if folder.Paused {
			indexHandlers.Remove(folder.ID)
			paused[cfg.ID] = struct{}{}
			continue
		}

		if cfg.Paused {
			indexHandlers.AddIndexInfo(folder.ID, ccDeviceInfos[folder.ID])
			continue
		}

		if err := m.ccCheckEncryption(cfg, folderDevice, ccDeviceInfos[folder.ID], deviceCfg.Untrusted); err != nil {
			sameError := false
			if devs, ok := m.folderEncryptionFailures[folder.ID]; ok {
				sameError = devs[deviceID] == err
			} else {
				m.folderEncryptionFailures[folder.ID] = make(map[protocol.DeviceID]error)
			}
			m.folderEncryptionFailures[folder.ID][deviceID] = err
			msg := fmt.Sprintf("Failure checking encryption consistency with device %v for folder %v: %v", deviceID, cfg.Description(), err)
			if sameError {
				l.Debugln(msg)
			} else {
				if rerr, ok := err.(*redactedError); ok {
					err = rerr.redacted
				}
				m.evLogger.Log(events.Failure, err.Error())
				l.Warnln(msg)
			}
			return tempIndexFolders, paused, err
		}
		if devErrs, ok := m.folderEncryptionFailures[folder.ID]; ok {
			if len(devErrs) == 1 {
				delete(m.folderEncryptionFailures, folder.ID)
			} else {
				delete(m.folderEncryptionFailures[folder.ID], deviceID)
			}
		}

		// Handle indexes

		if !folder.DisableTempIndexes {
			tempIndexFolders = append(tempIndexFolders, folder.ID)
		}

		indexHandlers.AddIndexInfo(folder.ID, ccDeviceInfos[folder.ID])
	}

	indexHandlers.RemoveAllExcept(seenFolders)
	expiredPendingList := make([]map[string]string, 0, len(expiredPending))
	for folder := range expiredPending {
		if err = m.db.RemovePendingFolderForDevice(folder, deviceID); err != nil {
			msg := "Failed to remove pending folder-device entry"
			l.Warnf("%v (%v, %v): %v", msg, folder, deviceID, err)
			m.evLogger.Log(events.Failure, msg)
			continue
		}
		expiredPendingList = append(expiredPendingList, map[string]string{
			"folderID": folder,
			"deviceID": deviceID.String(),
		})
	}
<|edits_diff|>
--- lib/model/model.go
+++ lib/model/model.go
@@ -20,12 +20,14 @@

 		if err := m.ccCheckEncryption(cfg, folderDevice, ccDeviceInfos[folder.ID], deviceCfg.Untrusted); err != nil {
 			sameError := false
+			m.fmut.Lock()
 			if devs, ok := m.folderEncryptionFailures[folder.ID]; ok {
 				sameError = devs[deviceID] == err
 			} else {
 				m.folderEncryptionFailures[folder.ID] = make(map[protocol.DeviceID]error)
 			}
 			m.folderEncryptionFailures[folder.ID][deviceID] = err
+			m.fmut.Unlock()
 			msg := fmt.Sprintf("Failure checking encryption consistency with device %v for folder %v: %v", deviceID, cfg.Description(), err)
 			if sameError {
 				l.Debugln(msg)
@@ -38,6 +40,7 @@
 			}
 			return tempIndexFolders, paused, err
 		}
+		m.fmut.Lock()
 		if devErrs, ok := m.folderEncryptionFailures[folder.ID]; ok {
 			if len(devErrs) == 1 {
 				delete(m.folderEncryptionFailures, folder.ID)
<|current_version|>
			m.evLogger.Log(events.FolderRejected, map[string]string{
				"folder":      folder.ID,
				"folderLabel": folder.Label,
				"device":      deviceID.String(),
			})
			l.Infof("Unexpected folder %s sent from device %q; ensure that the folder exists and that this device is selected under \"Share With\" in the folder configuration.", folder.Description(), deviceID)
			continue
		}

		if folder.Paused {
			indexHandlers.Remove(folder.ID)
			paused[cfg.ID] = struct{}{}
			continue
		}

		if cfg.Paused {
			indexHandlers.AddIndexInfo(folder.ID, ccDeviceInfos[folder.ID])
			continue
		}

		if err := m.ccCheckEncryption(cfg, folderDevice, ccDeviceInfos[folder.ID], deviceCfg.Untrusted); err != nil {
			sameError := false
			m.fmut.Lock()
			if devs, ok := m.folderEncryptionFailures[folder.ID]; ok {
				sameError = devs[deviceID] == err
			} else {
				m.folderEncryptionFailures[folder.ID] = make(map[protocol.DeviceID]error)
			}
			m.folderEncryptionFailures[folder.ID][deviceID] = err
			m.fmut.Unlock()
			msg := fmt.Sprintf("Failure checking encryption consistency with device %v for folder %v: %v", deviceID, cfg.Description(), err)
			if sameError {
				l.Debugln(msg)
			} else {
				if rerr, ok := err.(*redactedError); ok {
					err = rerr.redacted
				}
				m.evLogger.Log(events.Failure, err.Error())
				l.Warnln(msg)
			}
			return tempIndexFolders, paused, err
		}
		m.fmut.Lock()
		if devErrs, ok := m.folderEncryptionFailures[folder.ID]; ok {
			if len(devErrs) == 1 {
				delete(m.folderEncryptionFailures, folder.ID)
			} else {
				delete(m.folderEncryptionFailures[folder.ID], deviceID)
			}
		}

		// Handle indexes

		if !folder.DisableTempIndexes {
			tempIndexFolders = append(tempIndexFolders, folder.ID)
		}

		indexHandlers.AddIndexInfo(folder.ID, ccDeviceInfos[folder.ID])
	}

	indexHandlers.RemoveAllExcept(seenFolders)
	expiredPendingList := make([]map[string]string, 0, len(expiredPending))
	for folder := range expiredPending {
		if err = m.db.RemovePendingFolderForDevice(folder, deviceID); err != nil {
			msg := "Failed to remove pending folder-device entry"
			l.Warnf("%v (%v, %v): %v", msg, folder, deviceID, err)
			m.evLogger.Log(events.Failure, msg)
			continue
		}
		expiredPendingList = append(expiredPendingList, map[string]string{
			"folderID": folder,
			"deviceID": deviceID.String(),
		})
	}
<|next_version|>
			m.evLogger.Log(events.FolderRejected, map[string]string{
				"folder":      folder.ID,
				"folderLabel": folder.Label,
				"device":      deviceID.String(),
			})
			l.Infof("Unexpected folder %s sent from device %q; ensure that the folder exists and that this device is selected under \"Share With\" in the folder configuration.", folder.Description(), deviceID)
			continue
		}

		if folder.Paused {
			indexHandlers.Remove(folder.ID)
			paused[cfg.ID] = struct{}{}
			continue
		}

		if cfg.Paused {
			indexHandlers.AddIndexInfo(folder.ID, ccDeviceInfos[folder.ID])
			continue
		}

		if err := m.ccCheckEncryption(cfg, folderDevice, ccDeviceInfos[folder.ID], deviceCfg.Untrusted); err != nil {
			sameError := false
			m.fmut.Lock()
			if devs, ok := m.folderEncryptionFailures[folder.ID]; ok {
				sameError = devs[deviceID] == err
			} else {
				m.folderEncryptionFailures[folder.ID] = make(map[protocol.DeviceID]error)
			}
			m.folderEncryptionFailures[folder.ID][deviceID] = err
			m.fmut.Unlock()
			msg := fmt.Sprintf("Failure checking encryption consistency with device %v for folder %v: %v", deviceID, cfg.Description(), err)
			if sameError {
				l.Debugln(msg)
			} else {
				if rerr, ok := err.(*redactedError); ok {
					err = rerr.redacted
				}
				m.evLogger.Log(events.Failure, err.Error())
				l.Warnln(msg)
			}
			return tempIndexFolders, paused, err
		}
		m.fmut.Lock()
		if devErrs, ok := m.folderEncryptionFailures[folder.ID]; ok {
			if len(devErrs) == 1 {
				delete(m.folderEncryptionFailures, folder.ID)
			} else {
				delete(m.folderEncryptionFailures[folder.ID], deviceID)
			}
		}
		m.fmut.Unlock()

		// Handle indexes

		if !folder.DisableTempIndexes {
			tempIndexFolders = append(tempIndexFolders, folder.ID)
		}

		indexHandlers.AddIndexInfo(folder.ID, ccDeviceInfos[folder.ID])
	}

	indexHandlers.RemoveAllExcept(seenFolders)
	expiredPendingList := make([]map[string]string, 0, len(expiredPending))
	for folder := range expiredPending {
		if err = m.db.RemovePendingFolderForDevice(folder, deviceID); err != nil {
			msg := "Failed to remove pending folder-device entry"
			l.Warnf("%v (%v, %v): %v", msg, folder, deviceID, err)
			m.evLogger.Log(events.Failure, msg)
			continue
		}
		expiredPendingList = append(expiredPendingList, map[string]string{
			"folderID": folder,
			"deviceID": deviceID.String(),
		})
	}


all: Add untrusted folders behind feature flag (ref #62) (#7055)

<|original_code|>
// Copyright (C) 2016 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

package config

func (t FolderType) String() string {
	switch t {
	case FolderTypeSendReceive:
		return "sendreceive"
	case FolderTypeSendOnly:
		return "sendonly"
	case FolderTypeReceiveOnly:
		return "receiveonly"
	default:
		return "unknown"
	}
}

func (t FolderType) MarshalText() ([]byte, error) {
	return []byte(t.String()), nil
}

func (t *FolderType) UnmarshalText(bs []byte) error {
	switch string(bs) {
	case "readwrite", "sendreceive":
		*t = FolderTypeSendReceive
	case "readonly", "sendonly":
		*t = FolderTypeSendOnly
	case "receiveonly":
		*t = FolderTypeReceiveOnly
	default:
		*t = FolderTypeSendReceive
	}
	return nil
}

<|edits_diff|>
--- lib/config/foldertype.go
+++ lib/config/foldertype.go
@@ -14,6 +14,8 @@
 		return "sendonly"
 	case FolderTypeReceiveOnly:
 		return "receiveonly"
+	case FolderTypeReceiveEncrypted:
+		return "receiveencrypted"
 	default:
 		return "unknown"
 	}
<|current_version|>
// Copyright (C) 2016 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

package config

func (t FolderType) String() string {
	switch t {
	case FolderTypeSendReceive:
		return "sendreceive"
	case FolderTypeSendOnly:
		return "sendonly"
	case FolderTypeReceiveOnly:
		return "receiveonly"
	case FolderTypeReceiveEncrypted:
		return "receiveencrypted"
	default:
		return "unknown"
	}
}

func (t FolderType) MarshalText() ([]byte, error) {
	return []byte(t.String()), nil
}

func (t *FolderType) UnmarshalText(bs []byte) error {
	switch string(bs) {
	case "readwrite", "sendreceive":
		*t = FolderTypeSendReceive
	case "readonly", "sendonly":
		*t = FolderTypeSendOnly
	case "receiveonly":
		*t = FolderTypeReceiveOnly
	default:
		*t = FolderTypeSendReceive
	}
	return nil
}

<|next_version|>
// Copyright (C) 2016 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

package config

func (t FolderType) String() string {
	switch t {
	case FolderTypeSendReceive:
		return "sendreceive"
	case FolderTypeSendOnly:
		return "sendonly"
	case FolderTypeReceiveOnly:
		return "receiveonly"
	case FolderTypeReceiveEncrypted:
		return "receiveencrypted"
	default:
		return "unknown"
	}
}

func (t FolderType) MarshalText() ([]byte, error) {
	return []byte(t.String()), nil
}

func (t *FolderType) UnmarshalText(bs []byte) error {
	switch string(bs) {
	case "readwrite", "sendreceive":
		*t = FolderTypeSendReceive
	case "readonly", "sendonly":
		*t = FolderTypeSendOnly
	case "receiveonly":
		*t = FolderTypeReceiveOnly
	case "receiveencrypted":
		*t = FolderTypeReceiveEncrypted
	default:
		*t = FolderTypeSendReceive
	}
	return nil
}


lib/model: Check dir before deletion when pulling (#6741)

<|original_code|>
const ModeSetuid = FileMode(os.ModeSetuid)
const ModeSticky = FileMode(os.ModeSticky)
const ModeSymlink = FileMode(os.ModeSymlink)
const ModeType = FileMode(os.ModeType)
const PathSeparator = os.PathSeparator
const OptAppend = os.O_APPEND
const OptCreate = os.O_CREATE
const OptExclusive = os.O_EXCL
const OptReadOnly = os.O_RDONLY
const OptReadWrite = os.O_RDWR
const OptSync = os.O_SYNC
const OptTruncate = os.O_TRUNC
const OptWriteOnly = os.O_WRONLY

// SkipDir is used as a return value from WalkFuncs to indicate that
// the directory named in the call is to be skipped. It is not returned
// as an error by any function.
var SkipDir = filepath.SkipDir

// IsExist is the equivalent of os.IsExist
var IsExist = os.IsExist

// IsNotExist is the equivalent of os.IsNotExist
var IsNotExist = os.IsNotExist

// IsPermission is the equivalent of os.IsPermission
var IsPermission = os.IsPermission

// IsPathSeparator is the equivalent of os.IsPathSeparator
var IsPathSeparator = os.IsPathSeparator

func NewFilesystem(fsType FilesystemType, uri string) Filesystem {
	var fs Filesystem
	switch fsType {
	case FilesystemTypeBasic:
		fs = newBasicFilesystem(uri)
	case FilesystemTypeFake:
		fs = newFakeFilesystem(uri)
	default:
		l.Debugln("Unknown filesystem", fsType, uri)
		fs = &errorFilesystem{
			fsType: fsType,
			uri:    uri,
			err:    errors.New("filesystem with type " + fsType.String() + " does not exist."),
		}
	}

	if l.ShouldDebug("walkfs") {
<|edits_diff|>
--- lib/fs/filesystem.go
+++ lib/fs/filesystem.go
@@ -19,6 +19,9 @@

 // IsExist is the equivalent of os.IsExist
 var IsExist = os.IsExist
+
+// IsExist is the equivalent of os.ErrExist
+var ErrExist = os.ErrExist

 // IsNotExist is the equivalent of os.IsNotExist
 var IsNotExist = os.IsNotExist
<|current_version|>
const ModeSetuid = FileMode(os.ModeSetuid)
const ModeSticky = FileMode(os.ModeSticky)
const ModeSymlink = FileMode(os.ModeSymlink)
const ModeType = FileMode(os.ModeType)
const PathSeparator = os.PathSeparator
const OptAppend = os.O_APPEND
const OptCreate = os.O_CREATE
const OptExclusive = os.O_EXCL
const OptReadOnly = os.O_RDONLY
const OptReadWrite = os.O_RDWR
const OptSync = os.O_SYNC
const OptTruncate = os.O_TRUNC
const OptWriteOnly = os.O_WRONLY

// SkipDir is used as a return value from WalkFuncs to indicate that
// the directory named in the call is to be skipped. It is not returned
// as an error by any function.
var SkipDir = filepath.SkipDir

// IsExist is the equivalent of os.IsExist
var IsExist = os.IsExist

// IsExist is the equivalent of os.ErrExist
var ErrExist = os.ErrExist

// IsNotExist is the equivalent of os.IsNotExist
var IsNotExist = os.IsNotExist

// IsPermission is the equivalent of os.IsPermission
var IsPermission = os.IsPermission

// IsPathSeparator is the equivalent of os.IsPathSeparator
var IsPathSeparator = os.IsPathSeparator

func NewFilesystem(fsType FilesystemType, uri string) Filesystem {
	var fs Filesystem
	switch fsType {
	case FilesystemTypeBasic:
		fs = newBasicFilesystem(uri)
	case FilesystemTypeFake:
		fs = newFakeFilesystem(uri)
	default:
		l.Debugln("Unknown filesystem", fsType, uri)
		fs = &errorFilesystem{
			fsType: fsType,
			uri:    uri,
			err:    errors.New("filesystem with type " + fsType.String() + " does not exist."),
		}
	}

	if l.ShouldDebug("walkfs") {
<|next_version|>
const ModeSetuid = FileMode(os.ModeSetuid)
const ModeSticky = FileMode(os.ModeSticky)
const ModeSymlink = FileMode(os.ModeSymlink)
const ModeType = FileMode(os.ModeType)
const PathSeparator = os.PathSeparator
const OptAppend = os.O_APPEND
const OptCreate = os.O_CREATE
const OptExclusive = os.O_EXCL
const OptReadOnly = os.O_RDONLY
const OptReadWrite = os.O_RDWR
const OptSync = os.O_SYNC
const OptTruncate = os.O_TRUNC
const OptWriteOnly = os.O_WRONLY

// SkipDir is used as a return value from WalkFuncs to indicate that
// the directory named in the call is to be skipped. It is not returned
// as an error by any function.
var SkipDir = filepath.SkipDir

// IsExist is the equivalent of os.IsExist
var IsExist = os.IsExist

// IsExist is the equivalent of os.ErrExist
var ErrExist = os.ErrExist

// IsNotExist is the equivalent of os.IsNotExist
var IsNotExist = os.IsNotExist

// ErrNotExist is the equivalent of os.ErrNotExist
var ErrNotExist = os.ErrNotExist

// IsPermission is the equivalent of os.IsPermission
var IsPermission = os.IsPermission

// IsPathSeparator is the equivalent of os.IsPathSeparator
var IsPathSeparator = os.IsPathSeparator

func NewFilesystem(fsType FilesystemType, uri string) Filesystem {
	var fs Filesystem
	switch fsType {
	case FilesystemTypeBasic:
		fs = newBasicFilesystem(uri)
	case FilesystemTypeFake:
		fs = newFakeFilesystem(uri)
	default:
		l.Debugln("Unknown filesystem", fsType, uri)
		fs = &errorFilesystem{
			fsType: fsType,
			uri:    uri,
			err:    errors.New("filesystem with type " + fsType.String() + " does not exist."),
		}
	}

	if l.ShouldDebug("walkfs") {


lib/model: Add global request limiter (fixes #6302) (#6303)

<|original_code|>
// Copyright (C) 2018 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

package model

import (
	"sync"
)

type byteSemaphore struct {
	max       int
	available int
	mut       sync.Mutex
	cond      *sync.Cond
}

func newByteSemaphore(max int) *byteSemaphore {
	s := byteSemaphore{
		max:       max,
		available: max,
	}
	s.cond = sync.NewCond(&s.mut)
	return &s
}

func (s *byteSemaphore) take(bytes int) {
	s.mut.Lock()
	if bytes > s.max {
		bytes = s.max
	}
	for bytes > s.available {
		s.cond.Wait()
		if bytes > s.max {
			bytes = s.max
		}
	}
	s.available -= bytes
	s.mut.Unlock()
}

func (s *byteSemaphore) give(bytes int) {
	s.mut.Lock()
	if bytes > s.max {
		bytes = s.max
	}
	if s.available+bytes > s.max {
		s.available = s.max
	} else {
		s.available += bytes
	}
	s.cond.Broadcast()
	s.mut.Unlock()
}

func (s *byteSemaphore) setCapacity(cap int) {
	s.mut.Lock()
	diff := cap - s.max
	s.max = cap
	s.available += diff
	if s.available < 0 {
		s.available = 0
	} else if s.available > s.max {
		s.available = s.max
	}
	s.cond.Broadcast()
	s.mut.Unlock()
}

<|edits_diff|>
--- lib/model/bytesemaphore.go
+++ lib/model/bytesemaphore.go
@@ -18,6 +18,9 @@
 }

 func newByteSemaphore(max int) *byteSemaphore {
+	if max < 0 {
+		max = 0
+	}
 	s := byteSemaphore{
 		max:       max,
 		available: max,
<|current_version|>
// Copyright (C) 2018 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

package model

import (
	"sync"
)

type byteSemaphore struct {
	max       int
	available int
	mut       sync.Mutex
	cond      *sync.Cond
}

func newByteSemaphore(max int) *byteSemaphore {
	if max < 0 {
		max = 0
	}
	s := byteSemaphore{
		max:       max,
		available: max,
	}
	s.cond = sync.NewCond(&s.mut)
	return &s
}

func (s *byteSemaphore) take(bytes int) {
	s.mut.Lock()
	if bytes > s.max {
		bytes = s.max
	}
	for bytes > s.available {
		s.cond.Wait()
		if bytes > s.max {
			bytes = s.max
		}
	}
	s.available -= bytes
	s.mut.Unlock()
}

func (s *byteSemaphore) give(bytes int) {
	s.mut.Lock()
	if bytes > s.max {
		bytes = s.max
	}
	if s.available+bytes > s.max {
		s.available = s.max
	} else {
		s.available += bytes
	}
	s.cond.Broadcast()
	s.mut.Unlock()
}

func (s *byteSemaphore) setCapacity(cap int) {
	s.mut.Lock()
	diff := cap - s.max
	s.max = cap
	s.available += diff
	if s.available < 0 {
		s.available = 0
	} else if s.available > s.max {
		s.available = s.max
	}
	s.cond.Broadcast()
	s.mut.Unlock()
}

<|next_version|>
// Copyright (C) 2018 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

package model

import (
	"sync"
)

type byteSemaphore struct {
	max       int
	available int
	mut       sync.Mutex
	cond      *sync.Cond
}

func newByteSemaphore(max int) *byteSemaphore {
	if max < 0 {
		max = 0
	}
	s := byteSemaphore{
		max:       max,
		available: max,
	}
	s.cond = sync.NewCond(&s.mut)
	return &s
}

func (s *byteSemaphore) take(bytes int) {
	s.mut.Lock()
	if bytes > s.max {
		bytes = s.max
	}
	for bytes > s.available {
		s.cond.Wait()
		if bytes > s.max {
			bytes = s.max
		}
	}
	s.available -= bytes
	s.mut.Unlock()
}

func (s *byteSemaphore) give(bytes int) {
	s.mut.Lock()
	if bytes > s.max {
		bytes = s.max
	}
	if s.available+bytes > s.max {
		s.available = s.max
	} else {
		s.available += bytes
	}
	s.cond.Broadcast()
	s.mut.Unlock()
}

func (s *byteSemaphore) setCapacity(cap int) {
	if cap < 0 {
		cap = 0
	}
	s.mut.Lock()
	diff := cap - s.max
	s.max = cap
	s.available += diff
	if s.available < 0 {
		s.available = 0
	} else if s.available > s.max {
		s.available = s.max
	}
	s.cond.Broadcast()
	s.mut.Unlock()
}


cmd/syncthing: Account receive only folders in usage report

<|original_code|>

	res["totFiles"] = totFiles
	res["folderMaxFiles"] = maxFiles
	res["totMiB"] = totBytes / 1024 / 1024
	res["folderMaxMiB"] = maxBytes / 1024 / 1024

	var mem runtime.MemStats
	runtime.ReadMemStats(&mem)
	res["memoryUsageMiB"] = (mem.Sys - mem.HeapReleased) / 1024 / 1024
	res["sha256Perf"] = cpuBench(5, 125*time.Millisecond, false)
	res["hashPerf"] = cpuBench(5, 125*time.Millisecond, true)

	bytes, err := memorySize()
	if err == nil {
		res["memorySize"] = bytes / 1024 / 1024
	}
	res["numCPU"] = runtime.NumCPU()

	var rescanIntvs []int
	folderUses := map[string]int{
		"sendonly":            0,
		"sendreceive":         0,
		"ignorePerms":         0,
		"ignoreDelete":        0,
		"autoNormalize":       0,
		"simpleVersioning":    0,
		"externalVersioning":  0,
		"staggeredVersioning": 0,
		"trashcanVersioning":  0,
	}
	for _, cfg := range cfg.Folders() {
		rescanIntvs = append(rescanIntvs, cfg.RescanIntervalS)

		switch cfg.Type {
		case config.FolderTypeSendOnly:
			folderUses["sendonly"]++
		case config.FolderTypeSendReceive:
			folderUses["sendreceive"]++
		}
		if cfg.IgnorePerms {
			folderUses["ignorePerms"]++
		}
		if cfg.IgnoreDelete {
			folderUses["ignoreDelete"]++
		}
		if cfg.AutoNormalize {
			folderUses["autoNormalize"]++
		}
		if cfg.Versioning.Type != "" {
			folderUses[cfg.Versioning.Type+"Versioning"]++
		}
	}
	sort.Ints(rescanIntvs)
	res["rescanIntvs"] = rescanIntvs
	res["folderUses"] = folderUses

	deviceUses := map[string]int{
		"introducer":       0,
		"customCertName":   0,
		"compressAlways":   0,
		"compressMetadata": 0,
		"compressNever":    0,
<|edits_diff|>
--- cmd/syncthing/usage_report.go
+++ cmd/syncthing/usage_report.go
@@ -20,6 +20,7 @@
 	folderUses := map[string]int{
 		"sendonly":            0,
 		"sendreceive":         0,
+		"receiveonly":         0,
 		"ignorePerms":         0,
 		"ignoreDelete":        0,
 		"autoNormalize":       0,
<|current_version|>

	res["totFiles"] = totFiles
	res["folderMaxFiles"] = maxFiles
	res["totMiB"] = totBytes / 1024 / 1024
	res["folderMaxMiB"] = maxBytes / 1024 / 1024

	var mem runtime.MemStats
	runtime.ReadMemStats(&mem)
	res["memoryUsageMiB"] = (mem.Sys - mem.HeapReleased) / 1024 / 1024
	res["sha256Perf"] = cpuBench(5, 125*time.Millisecond, false)
	res["hashPerf"] = cpuBench(5, 125*time.Millisecond, true)

	bytes, err := memorySize()
	if err == nil {
		res["memorySize"] = bytes / 1024 / 1024
	}
	res["numCPU"] = runtime.NumCPU()

	var rescanIntvs []int
	folderUses := map[string]int{
		"sendonly":            0,
		"sendreceive":         0,
		"receiveonly":         0,
		"ignorePerms":         0,
		"ignoreDelete":        0,
		"autoNormalize":       0,
		"simpleVersioning":    0,
		"externalVersioning":  0,
		"staggeredVersioning": 0,
		"trashcanVersioning":  0,
	}
	for _, cfg := range cfg.Folders() {
		rescanIntvs = append(rescanIntvs, cfg.RescanIntervalS)

		switch cfg.Type {
		case config.FolderTypeSendOnly:
			folderUses["sendonly"]++
		case config.FolderTypeSendReceive:
			folderUses["sendreceive"]++
		}
		if cfg.IgnorePerms {
			folderUses["ignorePerms"]++
		}
		if cfg.IgnoreDelete {
			folderUses["ignoreDelete"]++
		}
		if cfg.AutoNormalize {
			folderUses["autoNormalize"]++
		}
		if cfg.Versioning.Type != "" {
			folderUses[cfg.Versioning.Type+"Versioning"]++
		}
	}
	sort.Ints(rescanIntvs)
	res["rescanIntvs"] = rescanIntvs
	res["folderUses"] = folderUses

	deviceUses := map[string]int{
		"introducer":       0,
		"customCertName":   0,
		"compressAlways":   0,
		"compressMetadata": 0,
		"compressNever":    0,
<|next_version|>

	res["totFiles"] = totFiles
	res["folderMaxFiles"] = maxFiles
	res["totMiB"] = totBytes / 1024 / 1024
	res["folderMaxMiB"] = maxBytes / 1024 / 1024

	var mem runtime.MemStats
	runtime.ReadMemStats(&mem)
	res["memoryUsageMiB"] = (mem.Sys - mem.HeapReleased) / 1024 / 1024
	res["sha256Perf"] = cpuBench(5, 125*time.Millisecond, false)
	res["hashPerf"] = cpuBench(5, 125*time.Millisecond, true)

	bytes, err := memorySize()
	if err == nil {
		res["memorySize"] = bytes / 1024 / 1024
	}
	res["numCPU"] = runtime.NumCPU()

	var rescanIntvs []int
	folderUses := map[string]int{
		"sendonly":            0,
		"sendreceive":         0,
		"receiveonly":         0,
		"ignorePerms":         0,
		"ignoreDelete":        0,
		"autoNormalize":       0,
		"simpleVersioning":    0,
		"externalVersioning":  0,
		"staggeredVersioning": 0,
		"trashcanVersioning":  0,
	}
	for _, cfg := range cfg.Folders() {
		rescanIntvs = append(rescanIntvs, cfg.RescanIntervalS)

		switch cfg.Type {
		case config.FolderTypeSendOnly:
			folderUses["sendonly"]++
		case config.FolderTypeSendReceive:
			folderUses["sendreceive"]++
		case config.FolderTypeReceiveOnly:
			folderUses["receiveonly"]++
		}
		if cfg.IgnorePerms {
			folderUses["ignorePerms"]++
		}
		if cfg.IgnoreDelete {
			folderUses["ignoreDelete"]++
		}
		if cfg.AutoNormalize {
			folderUses["autoNormalize"]++
		}
		if cfg.Versioning.Type != "" {
			folderUses[cfg.Versioning.Type+"Versioning"]++
		}
	}
	sort.Ints(rescanIntvs)
	res["rescanIntvs"] = rescanIntvs
	res["folderUses"] = folderUses

	deviceUses := map[string]int{
		"introducer":       0,
		"customCertName":   0,
		"compressAlways":   0,
		"compressMetadata": 0,
		"compressNever":    0,


Fix memory leak in  find_lib  for some invalid inputs

<|original_code|>
    }
    jv_free(x);
  }
  jv_free(components);
  return name;
}

// Assumes name has been validated
static jv jv_basename(jv name) {
  const char *s = jv_string_value(name);
  const char *p = strrchr(s, '/');
  if (!p)
    return name;
  jv res = jv_string_fmt("%s", p);
  jv_free(name);
  return res;
}

// Asummes validated relative path to module
static jv find_lib(jq_state *jq, jv rel_path, jv search, const char *suffix, jv jq_origin, jv lib_origin) {
  if (!jv_is_valid(rel_path)) {
    jv_free(search);
    return rel_path;
  }
  if (jv_get_kind(rel_path) != JV_KIND_STRING) {
    jv_free(rel_path);
    jv_free(search);
    return jv_invalid_with_msg(jv_string_fmt("Module path must be a string"));
  }
  if (jv_get_kind(search) != JV_KIND_ARRAY) {
    jv_free(rel_path);
    jv_free(search);
    return jv_invalid_with_msg(jv_string_fmt("Module search path must be an array"));
  }

  struct stat st;
  int ret;

  // Ideally we should cache this somewhere
  search = build_lib_search_chain(jq, search, jq_origin, lib_origin);
  jv err = jv_array_get(jv_copy(search), 1);
  search = jv_array_get(search, 0);

  jv bname = jv_basename(jv_copy(rel_path));

  jv_array_foreach(search, i, spath) {
    if (jv_get_kind(spath) == JV_KIND_NULL) {
      jv_free(spath);
      break;
    }
    if (jv_get_kind(spath) != JV_KIND_STRING ||
        strcmp(jv_string_value(spath), "") == 0) {
      jv_free(spath);
      continue; /* XXX report non-strings in search path?? */
    }
    // Try ${search_dir}/${rel_path}.jq
<|edits_diff|>
--- src/linker.c
+++ src/linker.c
@@ -20,11 +20,15 @@
 static jv find_lib(jq_state *jq, jv rel_path, jv search, const char *suffix, jv jq_origin, jv lib_origin) {
   if (!jv_is_valid(rel_path)) {
     jv_free(search);
+    jv_free(jq_origin);
+    jv_free(lib_origin);
     return rel_path;
   }
   if (jv_get_kind(rel_path) != JV_KIND_STRING) {
     jv_free(rel_path);
     jv_free(search);
+    jv_free(jq_origin);
+    jv_free(lib_origin);
     return jv_invalid_with_msg(jv_string_fmt("Module path must be a string"));
   }
   if (jv_get_kind(search) != JV_KIND_ARRAY) {
<|current_version|>
    }
    jv_free(x);
  }
  jv_free(components);
  return name;
}

// Assumes name has been validated
static jv jv_basename(jv name) {
  const char *s = jv_string_value(name);
  const char *p = strrchr(s, '/');
  if (!p)
    return name;
  jv res = jv_string_fmt("%s", p);
  jv_free(name);
  return res;
}

// Asummes validated relative path to module
static jv find_lib(jq_state *jq, jv rel_path, jv search, const char *suffix, jv jq_origin, jv lib_origin) {
  if (!jv_is_valid(rel_path)) {
    jv_free(search);
    jv_free(jq_origin);
    jv_free(lib_origin);
    return rel_path;
  }
  if (jv_get_kind(rel_path) != JV_KIND_STRING) {
    jv_free(rel_path);
    jv_free(search);
    jv_free(jq_origin);
    jv_free(lib_origin);
    return jv_invalid_with_msg(jv_string_fmt("Module path must be a string"));
  }
  if (jv_get_kind(search) != JV_KIND_ARRAY) {
    jv_free(rel_path);
    jv_free(search);
    return jv_invalid_with_msg(jv_string_fmt("Module search path must be an array"));
  }

  struct stat st;
  int ret;

  // Ideally we should cache this somewhere
  search = build_lib_search_chain(jq, search, jq_origin, lib_origin);
  jv err = jv_array_get(jv_copy(search), 1);
  search = jv_array_get(search, 0);

  jv bname = jv_basename(jv_copy(rel_path));

  jv_array_foreach(search, i, spath) {
    if (jv_get_kind(spath) == JV_KIND_NULL) {
      jv_free(spath);
      break;
    }
    if (jv_get_kind(spath) != JV_KIND_STRING ||
        strcmp(jv_string_value(spath), "") == 0) {
      jv_free(spath);
      continue; /* XXX report non-strings in search path?? */
    }
    // Try ${search_dir}/${rel_path}.jq
<|next_version|>
    }
    jv_free(x);
  }
  jv_free(components);
  return name;
}

// Assumes name has been validated
static jv jv_basename(jv name) {
  const char *s = jv_string_value(name);
  const char *p = strrchr(s, '/');
  if (!p)
    return name;
  jv res = jv_string_fmt("%s", p);
  jv_free(name);
  return res;
}

// Asummes validated relative path to module
static jv find_lib(jq_state *jq, jv rel_path, jv search, const char *suffix, jv jq_origin, jv lib_origin) {
  if (!jv_is_valid(rel_path)) {
    jv_free(search);
    jv_free(jq_origin);
    jv_free(lib_origin);
    return rel_path;
  }
  if (jv_get_kind(rel_path) != JV_KIND_STRING) {
    jv_free(rel_path);
    jv_free(search);
    jv_free(jq_origin);
    jv_free(lib_origin);
    return jv_invalid_with_msg(jv_string_fmt("Module path must be a string"));
  }
  if (jv_get_kind(search) != JV_KIND_ARRAY) {
    jv_free(rel_path);
    jv_free(search);
    jv_free(jq_origin);
    jv_free(lib_origin);
    return jv_invalid_with_msg(jv_string_fmt("Module search path must be an array"));
  }

  struct stat st;
  int ret;

  // Ideally we should cache this somewhere
  search = build_lib_search_chain(jq, search, jq_origin, lib_origin);
  jv err = jv_array_get(jv_copy(search), 1);
  search = jv_array_get(search, 0);

  jv bname = jv_basename(jv_copy(rel_path));

  jv_array_foreach(search, i, spath) {
    if (jv_get_kind(spath) == JV_KIND_NULL) {
      jv_free(spath);
      break;
    }
    if (jv_get_kind(spath) != JV_KIND_STRING ||
        strcmp(jv_string_value(spath), "") == 0) {
      jv_free(spath);
      continue; /* XXX report non-strings in search path?? */
    }
    // Try ${search_dir}/${rel_path}.jq