commit 11d2149f1de4c0975ce70f8eaceb863b9058675c
Author: David Fifield <[email protected]>
Date:   Sun Dec 8 01:59:57 2013 -0800

    parseServerTransportOptions.
---
 args.go      |   70 +++++++++++++++++++++++++++++++++++++
 args_test.go |  110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 180 insertions(+)

diff --git a/args.go b/args.go
index df3aced..a0a3bd5 100644
--- a/args.go
+++ b/args.go
@@ -103,3 +103,73 @@ func parseClientParameters(s string) (args Args, err 
error) {
        }
        return args, 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 = errors.New(fmt.Sprintf("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 = errors.New(fmt.Sprintf("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 = errors.New(fmt.Sprintf("empty method name in %q", 
s[begin:i]))
+                       return
+               }
+               if len(key) == 0 {
+                       err = errors.New(fmt.Sprintf("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
+}
diff --git a/args_test.go b/args_test.go
index 69194fd..078c79a 100644
--- a/args_test.go
+++ b/args_test.go
@@ -181,3 +181,113 @@ func TestParseClientParameters(t *testing.T) {
                }
        }
 }
+
+func optsEqual(a, b map[string]Args) bool {
+       for k, av := range a {
+               bv, ok := b[k]
+               if !ok || !argsEqual(av, bv) {
+                       return false
+               }
+       }
+       for k, bv := range b {
+               av, ok := a[k]
+               if !ok || !argsEqual(av, bv) {
+                       return false
+               }
+       }
+       return true
+}
+
+func TestParseServerTransportOptions(t *testing.T) {
+       badTests := [...]string{
+               ":=",
+               "t:=",
+               ":k=",
+               ":=v",
+               "t:=v",
+               "t:=v",
+               "t:k=v;",
+               "abc",
+               "t:",
+               "key=value",
+               "=value",
+               "t:k=v\\",
+               "t1:k=v;t2:k=v\\",
+               "t:=key=value",
+               "t:==key=value",
+               "t:;key=value",
+               "t:key\\=value",
+       }
+       goodTests := [...]struct {
+               input    string
+               expected map[string]Args
+       }{
+               {
+                       "",
+                       map[string]Args{},
+               },
+               {
+                       "t:k=v",
+                       map[string]Args{
+                               "t": Args{"k": []string{"v"}},
+                       },
+               },
+               {
+                       "t1:k=v1;t2:k=v2;t1:k=v3",
+                       map[string]Args{
+                               "t1": Args{"k": []string{"v1", "v3"}},
+                               "t2": Args{"k": []string{"v2"}},
+                       },
+               },
+               {
+                       "t\\:1:k=v;t\\=2:k=v;t\\;3:k=v;t\\\\4:k=v",
+                       map[string]Args{
+                               "t:1":  Args{"k": []string{"v"}},
+                               "t=2":  Args{"k": []string{"v"}},
+                               "t;3":  Args{"k": []string{"v"}},
+                               "t\\4": Args{"k": []string{"v"}},
+                       },
+               },
+               {
+                       "t:k\\:1=v;t:k\\=2=v;t:k\\;3=v;t:k\\\\4=v",
+                       map[string]Args{
+                               "t": Args{
+                                       "k:1":  []string{"v"},
+                                       "k=2":  []string{"v"},
+                                       "k;3":  []string{"v"},
+                                       "k\\4": []string{"v"},
+                               },
+                       },
+               },
+               {
+                       "t:k=v\\:1;t:k=v\\=2;t:k=v\\;3;t:k=v\\\\4",
+                       map[string]Args{
+                               "t": Args{"k": []string{"v:1", "v=2", "v;3", 
"v\\4"}},
+                       },
+               },
+               {
+                       
"trebuchet:secret=nou;trebuchet:cache=/tmp/cache;ballista:secret=yes",
+                       map[string]Args{
+                               "trebuchet": Args{"secret": []string{"nou"}, 
"cache": []string{"/tmp/cache"}},
+                               "ballista":  Args{"secret": []string{"yes"}},
+                       },
+               },
+       }
+
+       for _, input := range badTests {
+               _, err := parseServerTransportOptions(input)
+               if err == nil {
+                       t.Errorf("%q unexpectedly succeeded", input)
+               }
+       }
+
+       for _, test := range goodTests {
+               opts, err := parseServerTransportOptions(test.input)
+               if err != nil {
+                       t.Errorf("%q unexpectedly returned an error: %s", 
test.input, err)
+               }
+               if !optsEqual(opts, test.expected) {
+                       t.Errorf("%q → %q (expected %q)", test.input, opts, 
test.expected)
+               }
+       }
+}



_______________________________________________
tor-commits mailing list
[email protected]
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits

Reply via email to