func (uri *Pkcs11URI) SetAllowAnyModule(allowAnyModule bool) {
	uri.allowAnyModule = allowAnyModule
}
SetAllowAnyModule allows any module to be loaded; by default this is not allowed
package pkcs11uri
import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/url"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
)
type Pkcs11URI struct {
	pathAttributes  map[string]string
	queryAttributes map[string]string
	moduleDirectories []string
	allowedModulePaths []string
	allowAnyModule bool
	env map[string]string
}
const hex = "0123456789ABCDEF"
func escapeAll(s string) string {
	res := make([]byte, len(s)*3)
	j := 0
	for i := 0; i < len(s); i++ {
		c := s[i]
		res[j] = '%'
		res[j+1] = hex[c>>4]
		res[j+2] = hex[c&0xf]
		j += 3
	}
	return string(res)
}
func escape(s string, isPath bool) string {
	res := make([]byte, len(s)*3)
	j := 0
	for i := 0; i < len(s); i++ {
		c := s[i]
		if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') {
			res[j] = c
		} else if isPath && c == '&' {
			res[j] = c
		} else if !isPath && (c == '/' || c == '?' || c == '|') {
			res[j] = c
		} else {
			switch c {
			case '-', '.', '_', '~':  
				res[j] = c
			case ':', '[', ']', '@', '!', '$', '\'', '(', ')', '*', '+', ',', '=':
				res[j] = c
			default:
				res[j] = '%'
				res[j+1] = hex[c>>4]
				res[j+2] = hex[c&0xf]
				j += 2
			}
		}
		j++
	}
	return string(res[:j])
}
func New() *Pkcs11URI {
	return &Pkcs11URI{
		pathAttributes:  make(map[string]string),
		queryAttributes: make(map[string]string),
		env:             make(map[string]string),
	}
}
func (uri *Pkcs11URI) setAttribute(attrMap map[string]string, name, value string) error {
	v, err := url.PathUnescape(value)
	if err != nil {
		return err
	}
	attrMap[name] = v
	return nil
}
func (uri *Pkcs11URI) GetPathAttribute(name string, pctencode bool) (string, bool) {
	v, ok := uri.pathAttributes[name]
	if ok && pctencode {
		v = escape(v, true)
	}
	return v, ok
}
func (uri *Pkcs11URI) SetPathAttribute(name, value string) error {
	return uri.setAttribute(uri.pathAttributes, name, value)
}
func (uri *Pkcs11URI) AddPathAttribute(name, value string) error {
	if _, ok := uri.pathAttributes[name]; ok {
		return errors.New("duplicate path attribute")
	}
	return uri.SetPathAttribute(name, value)
}
func (uri *Pkcs11URI) RemovePathAttribute(name string) {
	delete(uri.pathAttributes, name)
}
func (uri *Pkcs11URI) AddEnv(name, value string) {
	uri.env[name] = value
}
func (uri *Pkcs11URI) SetEnvMap(env map[string]string) {
	uri.env = env
}
func (uri *Pkcs11URI) GetEnvMap() map[string]string {
	return uri.env
}
func (uri *Pkcs11URI) GetQueryAttribute(name string, pctencode bool) (string, bool) {
	v, ok := uri.queryAttributes[name]
	if ok && pctencode {
		v = escape(v, false)
	}
	return v, ok
}
func (uri *Pkcs11URI) SetQueryAttribute(name, value string) error {
	return uri.setAttribute(uri.queryAttributes, name, value)
}
func (uri *Pkcs11URI) AddQueryAttribute(name, value string) error {
	if _, ok := uri.queryAttributes[name]; ok {
		return errors.New("duplicate query attribute")
	}
	return uri.SetQueryAttribute(name, value)
}
func (uri *Pkcs11URI) RemoveQueryAttribute(name string) {
	delete(uri.queryAttributes, name)
}
func (uri *Pkcs11URI) Validate() error {
	if v, ok := uri.pathAttributes["slot-id"]; ok {
		if _, err := strconv.Atoi(v); err != nil {
			return fmt.Errorf("slot-id must be a number: %s", v)
		}
	}
	if v, ok := uri.pathAttributes["library-version"]; ok {
		m, err := regexp.Match("^[0-9]+(\\.[0-9]+)?$", []byte(v))
		if err != nil || !m {
			return fmt.Errorf("Invalid format for library-version '%s'", v)
		}
	}
	if v, ok := uri.pathAttributes["type"]; ok {
		m, err := regexp.Match("^(public|private|cert|secret-key}data)?$", []byte(v))
		if err != nil || !m {
			return fmt.Errorf("Invalid type '%s'", v)
		}
	}
	_, ok1 := uri.queryAttributes["pin-source"]
	_, ok2 := uri.queryAttributes["pin-value"]
	if ok1 && ok2 {
		return errors.New("URI must not contain pin-source and pin-value")
	}
	if v, ok := uri.queryAttributes["module-path"]; ok {
		if !filepath.IsAbs(v) {
			return fmt.Errorf("path %s of module-name attribute must be absolute", v)
		}
	}
	return nil
}
func (uri *Pkcs11URI) HasPIN() bool {
	_, ok := uri.queryAttributes["pin-value"]
	if ok {
		return true
	}
	_, ok = uri.queryAttributes["pin-source"]
	return ok
}
func (uri *Pkcs11URI) GetPIN() (string, error) {
	if v, ok := uri.queryAttributes["pin-value"]; ok {
		return v, nil
	}
	if v, ok := uri.queryAttributes["pin-source"]; ok {
		pinuri, err := url.ParseRequestURI(v)
		if err != nil {
			return "", fmt.Errorf("Could not parse pin-source: %s ", err)
		}
		switch pinuri.Scheme {
		case "", "file":
			if !filepath.IsAbs(pinuri.Path) {
				return "", fmt.Errorf("PIN URI path '%s' is not absolute", pinuri.Path)
			}
			pin, err := ioutil.ReadFile(pinuri.Path)
			if err != nil {
				return "", fmt.Errorf("Could not open PIN file: %s", err)
			}
			return string(pin), nil
		default:
			return "", fmt.Errorf("PIN URI scheme %s is not supported", pinuri.Scheme)
		}
	}
	return "", fmt.Errorf("Neither pin-source nor pin-value are available")
}
func (uri *Pkcs11URI) Parse(uristring string) error {
	if !strings.HasPrefix(uristring, "pkcs11:") {
		return errors.New("Malformed pkcs11 URI: missing pcks11: prefix")
	}
	parts := strings.SplitN(uristring[7:], "?", 2)
	uri.pathAttributes = make(map[string]string)
	uri.queryAttributes = make(map[string]string)
	if len(parts[0]) > 0 {
		for _, part := range strings.Split(parts[0], ";") {
			p := strings.SplitN(part, "=", 2)
			if len(p) != 2 {
				return errors.New("Malformed pkcs11 URI: malformed path attribute")
			}
			if err := uri.AddPathAttribute(p[0], p[1]); err != nil {
				return fmt.Errorf("Malformed pkcs11 URI: %s", err)
			}
		}
	}
	if len(parts) == 2 {
		for _, part := range strings.Split(parts[1], "&") {
			p := strings.SplitN(part, "=", 2)
			if len(p) != 2 {
				return errors.New("Malformed pkcs11 URI: malformed query attribute")
			}
			if err := uri.AddQueryAttribute(p[0], p[1]); err != nil {
				return fmt.Errorf("Malformed pkcs11 URI: %s", err)
			}
		}
	}
	return uri.Validate()
}
func formatAttributes(attrMap map[string]string, ispath bool) string {
	res := ""
	for key, value := range attrMap {
		switch key {
		case "id":
			value = escapeAll(value)
		default:
			if ispath {
				value = escape(value, true)
			} else {
				value = escape(value, false)
			}
		}
		if len(res) > 0 {
			if ispath {
				res += ";"
			} else {
				res += "&"
			}
		}
		res += key + "=" + value
	}
	return res
}
func (uri *Pkcs11URI) Format() (string, error) {
	if err := uri.Validate(); err != nil {
		return "", err
	}
	result := "pkcs11:" + formatAttributes(uri.pathAttributes, true)
	if len(uri.queryAttributes) > 0 {
		result += "?" + formatAttributes(uri.queryAttributes, false)
	}
	return result, nil
}
func (uri *Pkcs11URI) SetModuleDirectories(moduleDirectories []string) {
	uri.moduleDirectories = moduleDirectories
}
func (uri *Pkcs11URI) GetModuleDirectories() []string {
	return uri.moduleDirectories
}
func (uri *Pkcs11URI) SetAllowedModulePaths(allowedModulePaths []string) {
	uri.allowedModulePaths = allowedModulePaths
}