summaryrefslogtreecommitdiff
path: root/vendor/github.com/OperatorFoundation/shapeshifter-ipc/args.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/OperatorFoundation/shapeshifter-ipc/args.go')
-rw-r--r--vendor/github.com/OperatorFoundation/shapeshifter-ipc/args.go265
1 files changed, 265 insertions, 0 deletions
diff --git a/vendor/github.com/OperatorFoundation/shapeshifter-ipc/args.go b/vendor/github.com/OperatorFoundation/shapeshifter-ipc/args.go
new file mode 100644
index 0000000..295efa4
--- /dev/null
+++ b/vendor/github.com/OperatorFoundation/shapeshifter-ipc/args.go
@@ -0,0 +1,265 @@
+package pt
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "sort"
+ "strings"
+)
+
+// Key–value mappings for the representation of client and server options.
+
+// Args maps a string key to a list of values. It is similar to url.Values.
+type Args map[string][]string
+
+// Get the first value associated with the given key. If there are any values
+// associated with the key, the value return has the value and ok is set to
+// true. If there are no values for the given key, value is "" and ok is false.
+// If you need access to multiple values, use the map directly.
+func (args Args) Get(key string) (value string, ok bool) {
+ if args == nil {
+ return "", false
+ }
+ vals, ok := args[key]
+ if !ok || len(vals) == 0 {
+ return "", false
+ }
+ return vals[0], true
+}
+
+// Append value to the list of values for key.
+func (args Args) Add(key, value string) {
+ args[key] = append(args[key], value)
+}
+
+// Return the index of the next unescaped byte in s that is in the term set, or
+// else the length of the string if no terminators appear. Additionally return
+// the unescaped string up to the returned index.
+func indexUnescaped(s string, term []byte) (int, string, error) {
+ var i int
+ unesc := make([]byte, 0)
+ for i = 0; i < len(s); i++ {
+ b := s[i]
+ // A terminator byte?
+ if bytes.IndexByte(term, b) != -1 {
+ break
+ }
+ if b == '\\' {
+ i++
+ if i >= len(s) {
+ return 0, "", fmt.Errorf("nothing following final escape in %q", s)
+ }
+ b = s[i]
+ }
+ unesc = append(unesc, b)
+ }
+ return i, string(unesc), nil
+}
+
+// Parse a name–value mapping as from an encoded SOCKS username/password.
+//
+// "If any [k=v] items are provided, they are configuration parameters for the
+// proxy: Tor should separate them with semicolons ... If a key or value value
+// must contain [an equals sign or] a semicolon or a backslash, it is escaped
+// with a backslash."
+func parseClientParameters(s string) (args Args, err error) {
+ args = make(Args)
+ if len(s) == 0 {
+ return
+ }
+ i := 0
+ for {
+ var key, value string
+ var offset, begin int
+
+ begin = i
+ // Read the key.
+ offset, key, err = indexUnescaped(s[i:], []byte{'=', ';'})
+ if err != nil {
+ return
+ }
+ i += offset
+ // End of string or no equals sign?
+ if i >= len(s) || s[i] != '=' {
+ err = fmt.Errorf("no equals sign in %q", s[begin:i])
+ return
+ }
+ // Skip the equals sign.
+ i++
+ // Read the value.
+ offset, value, err = indexUnescaped(s[i:], []byte{';'})
+ if err != nil {
+ return
+ }
+ i += offset
+ if len(key) == 0 {
+ err = fmt.Errorf("empty key in %q", s[begin:i])
+ return
+ }
+ args.Add(key, value)
+ if i >= len(s) {
+ break
+ }
+ // Skip the semicolon.
+ i++
+ }
+ return args, nil
+}
+
+func ParsePT2ClientParameters(s string) (args Args, err error) {
+ args = make(Args)
+ if len(s) == 0 {
+ return
+ }
+
+ decoder := json.NewDecoder(strings.NewReader(s))
+ var result map[string]string
+ if err := decoder.Decode(&result); err != nil {
+ fmt.Errorf("Error decoding JSON %q", err)
+ return nil, err
+ }
+
+ for key, value := range result {
+ args.Add(key, value)
+ }
+
+ return args, nil
+}
+
+func ParsePT2ServerParameters(s string) (params map[string]Args, err error) {
+ opts := make(map[string]Args)
+ if len(s) == 0 {
+ return opts, nil
+ }
+
+ decoder := json.NewDecoder(strings.NewReader(s))
+ var result map[string]map[string]string
+ if err := decoder.Decode(&result); err != nil {
+ fmt.Errorf("Error decoding JSON %q", err)
+ return nil, err
+ }
+
+ for key, sub := range result {
+ args := make(Args)
+ for key2, value := range sub {
+ args.Add(key2, value)
+ }
+
+ opts[key] = args
+ }
+
+ return opts, nil
+}
+
+// Parse a transport–name–value mapping as from TOR_PT_SERVER_TRANSPORT_OPTIONS.
+//
+// "<value> is a k=v string value with options that are to be passed to the
+// transport. Colons, semicolons, equal signs and backslashes must be escaped
+// with a backslash."
+// Example: trebuchet:secret=nou;trebuchet:cache=/tmp/cache;ballista:secret=yes
+func ParseServerTransportOptions(s string) (opts map[string]Args, err error) {
+ opts = make(map[string]Args)
+ if len(s) == 0 {
+ return
+ }
+ i := 0
+ for {
+ var methodName, key, value string
+ var offset, begin int
+
+ begin = i
+ // Read the method name.
+ offset, methodName, err = indexUnescaped(s[i:], []byte{':', '=', ';'})
+ if err != nil {
+ return
+ }
+ i += offset
+ // End of string or no colon?
+ if i >= len(s) || s[i] != ':' {
+ err = fmt.Errorf("no colon in %q", s[begin:i])
+ return
+ }
+ // Skip the colon.
+ i++
+ // Read the key.
+ offset, key, err = indexUnescaped(s[i:], []byte{'=', ';'})
+ if err != nil {
+ return
+ }
+ i += offset
+ // End of string or no equals sign?
+ if i >= len(s) || s[i] != '=' {
+ err = fmt.Errorf("no equals sign in %q", s[begin:i])
+ return
+ }
+ // Skip the equals sign.
+ i++
+ // Read the value.
+ offset, value, err = indexUnescaped(s[i:], []byte{';'})
+ if err != nil {
+ return
+ }
+ i += offset
+ if len(methodName) == 0 {
+ err = fmt.Errorf("empty method name in %q", s[begin:i])
+ return
+ }
+ if len(key) == 0 {
+ err = fmt.Errorf("empty key in %q", s[begin:i])
+ return
+ }
+ if opts[methodName] == nil {
+ opts[methodName] = make(Args)
+ }
+ opts[methodName].Add(key, value)
+ if i >= len(s) {
+ break
+ }
+ // Skip the semicolon.
+ i++
+ }
+ return opts, nil
+}
+
+// Escape backslashes and all the bytes that are in set.
+func backslashEscape(s string, set []byte) string {
+ var buf bytes.Buffer
+ for _, b := range []byte(s) {
+ if b == '\\' || bytes.IndexByte(set, b) != -1 {
+ buf.WriteByte('\\')
+ }
+ buf.WriteByte(b)
+ }
+ return buf.String()
+}
+
+// Encode a name–value mapping so that it is suitable to go in the ARGS option
+// of an SMETHOD line. The output is sorted by key. The "ARGS:" prefix is not
+// added.
+//
+// "Equal signs and commas [and backslashes] must be escaped with a backslash."
+func encodeSmethodArgs(args Args) string {
+ if args == nil {
+ return ""
+ }
+
+ keys := make([]string, 0, len(args))
+ for key := range args {
+ keys = append(keys, key)
+ }
+ sort.Strings(keys)
+
+ escape := func(s string) string {
+ return backslashEscape(s, []byte{'=', ','})
+ }
+
+ var pairs []string
+ for _, key := range keys {
+ for _, value := range args[key] {
+ pairs = append(pairs, escape(key)+"="+escape(value))
+ }
+ }
+
+ return strings.Join(pairs, ",")
+}