args.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. package sip003
  2. import (
  3. "bytes"
  4. "fmt"
  5. )
  6. // mod from https://github.com/shadowsocks/v2ray-plugin/blob/master/args.go
  7. // Args maps a string key to a list of values. It is similar to url.Values.
  8. type Args map[string][]string
  9. // Get the first value associated with the given key. If there are any values
  10. // associated with the key, the value return has the value and ok is set to
  11. // true. If there are no values for the given key, value is "" and ok is false.
  12. // If you need access to multiple values, use the map directly.
  13. func (args Args) Get(key string) (value string, ok bool) {
  14. if args == nil {
  15. return "", false
  16. }
  17. vals, ok := args[key]
  18. if !ok || len(vals) == 0 {
  19. return "", false
  20. }
  21. return vals[0], true
  22. }
  23. // Add Append value to the list of values for key.
  24. func (args Args) Add(key, value string) {
  25. args[key] = append(args[key], value)
  26. }
  27. // Return the index of the next unescaped byte in s that is in the term set, or
  28. // else the length of the string if no terminators appear. Additionally return
  29. // the unescaped string up to the returned index.
  30. func indexUnescaped(s string, term []byte) (int, string, error) {
  31. var i int
  32. unesc := make([]byte, 0)
  33. for i = 0; i < len(s); i++ {
  34. b := s[i]
  35. // A terminator byte?
  36. if bytes.IndexByte(term, b) != -1 {
  37. break
  38. }
  39. if b == '\\' {
  40. i++
  41. if i >= len(s) {
  42. return 0, "", fmt.Errorf("nothing following final escape in %q", s)
  43. }
  44. b = s[i]
  45. }
  46. unesc = append(unesc, b)
  47. }
  48. return i, string(unesc), nil
  49. }
  50. // ParsePluginOptions Parse a name–value mapping as from SS_PLUGIN_OPTIONS.
  51. //
  52. // "<value> is a k=v string value with options that are to be passed to the
  53. // transport. semicolons, equal signs and backslashes must be escaped
  54. // with a backslash."
  55. // Example: secret=nou;cache=/tmp/cache;secret=yes
  56. func ParsePluginOptions(s string) (opts Args, err error) {
  57. opts = make(Args)
  58. if len(s) == 0 {
  59. return
  60. }
  61. i := 0
  62. for {
  63. var key, value string
  64. var offset, begin int
  65. if i >= len(s) {
  66. break
  67. }
  68. begin = i
  69. // Read the key.
  70. offset, key, err = indexUnescaped(s[i:], []byte{'=', ';'})
  71. if err != nil {
  72. return
  73. }
  74. if len(key) == 0 {
  75. err = fmt.Errorf("empty key in %q", s[begin:i])
  76. return
  77. }
  78. i += offset
  79. // End of string or no equals sign?
  80. if i >= len(s) || s[i] != '=' {
  81. opts.Add(key, "1")
  82. // Skip the semicolon.
  83. i++
  84. continue
  85. }
  86. // Skip the equals sign.
  87. i++
  88. // Read the value.
  89. offset, value, err = indexUnescaped(s[i:], []byte{';'})
  90. if err != nil {
  91. return
  92. }
  93. i += offset
  94. opts.Add(key, value)
  95. // Skip the semicolon.
  96. i++
  97. }
  98. return opts, nil
  99. }
  100. // Escape backslashes and all the bytes that are in set.
  101. func backslashEscape(s string, set []byte) string {
  102. var buf bytes.Buffer
  103. for _, b := range []byte(s) {
  104. if b == '\\' || bytes.IndexByte(set, b) != -1 {
  105. buf.WriteByte('\\')
  106. }
  107. buf.WriteByte(b)
  108. }
  109. return buf.String()
  110. }