escape.go 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. package dbus
  2. import "net/url"
  3. // EscapeBusAddressValue implements a requirement to escape the values
  4. // in D-Bus server addresses, as defined by the D-Bus specification at
  5. // https://dbus.freedesktop.org/doc/dbus-specification.html#addresses.
  6. func EscapeBusAddressValue(val string) string {
  7. toEsc := strNeedsEscape(val)
  8. if toEsc == 0 {
  9. // Avoid unneeded allocation/copying.
  10. return val
  11. }
  12. // Avoid allocation for short paths.
  13. var buf [64]byte
  14. var out []byte
  15. // Every to-be-escaped byte needs 2 extra bytes.
  16. required := len(val) + 2*toEsc
  17. if required <= len(buf) {
  18. out = buf[:required]
  19. } else {
  20. out = make([]byte, required)
  21. }
  22. j := 0
  23. for i := 0; i < len(val); i++ {
  24. if ch := val[i]; needsEscape(ch) {
  25. // Convert ch to %xx, where xx is hex value.
  26. out[j] = '%'
  27. out[j+1] = hexchar(ch >> 4)
  28. out[j+2] = hexchar(ch & 0x0F)
  29. j += 3
  30. } else {
  31. out[j] = ch
  32. j++
  33. }
  34. }
  35. return string(out)
  36. }
  37. // UnescapeBusAddressValue unescapes values in D-Bus server addresses,
  38. // as defined by the D-Bus specification at
  39. // https://dbus.freedesktop.org/doc/dbus-specification.html#addresses.
  40. func UnescapeBusAddressValue(val string) (string, error) {
  41. // Looks like url.PathUnescape does exactly what is required.
  42. return url.PathUnescape(val)
  43. }
  44. // hexchar returns an octal representation of a n, where n < 16.
  45. // For invalid values of n, the function panics.
  46. func hexchar(n byte) byte {
  47. const hex = "0123456789abcdef"
  48. // For n >= len(hex), runtime will panic.
  49. return hex[n]
  50. }
  51. // needsEscape tells if a byte is NOT one of optionally-escaped bytes.
  52. func needsEscape(c byte) bool {
  53. if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' {
  54. return false
  55. }
  56. switch c {
  57. case '-', '_', '/', '\\', '.', '*':
  58. return false
  59. }
  60. return true
  61. }
  62. // strNeedsEscape tells how many bytes in the string need escaping.
  63. func strNeedsEscape(val string) int {
  64. count := 0
  65. for i := 0; i < len(val); i++ {
  66. if needsEscape(val[i]) {
  67. count++
  68. }
  69. }
  70. return count
  71. }