go-tg-screenshot-bot
293 строки · 6.8 Кб
1package dbus2
3import (4"fmt"5"reflect"6"strings"7)
8
9var sigToType = map[byte]reflect.Type{10'y': byteType,11'b': boolType,12'n': int16Type,13'q': uint16Type,14'i': int32Type,15'u': uint32Type,16'x': int64Type,17't': uint64Type,18'd': float64Type,19's': stringType,20'g': signatureType,21'o': objectPathType,22'v': variantType,23'h': unixFDIndexType,24}
25
26// Signature represents a correct type signature as specified by the D-Bus
27// specification. The zero value represents the empty signature, "".
28type Signature struct {29str string30}
31
32// SignatureOf returns the concatenation of all the signatures of the given
33// values. It panics if one of them is not representable in D-Bus.
34func SignatureOf(vs ...interface{}) Signature {35var s string36for _, v := range vs {37s += getSignature(reflect.TypeOf(v), &depthCounter{})38}39return Signature{s}40}
41
42// SignatureOfType returns the signature of the given type. It panics if the
43// type is not representable in D-Bus.
44func SignatureOfType(t reflect.Type) Signature {45return Signature{getSignature(t, &depthCounter{})}46}
47
48// getSignature returns the signature of the given type and panics on unknown types.
49func getSignature(t reflect.Type, depth *depthCounter) (sig string) {50if !depth.Valid() {51panic("container nesting too deep")52}53defer func() {54if len(sig) > 255 {55panic("signature exceeds the length limitation")56}57}()58// handle simple types first59switch t.Kind() {60case reflect.Uint8:61return "y"62case reflect.Bool:63return "b"64case reflect.Int16:65return "n"66case reflect.Uint16:67return "q"68case reflect.Int, reflect.Int32:69if t == unixFDType {70return "h"71}72return "i"73case reflect.Uint, reflect.Uint32:74if t == unixFDIndexType {75return "h"76}77return "u"78case reflect.Int64:79return "x"80case reflect.Uint64:81return "t"82case reflect.Float64:83return "d"84case reflect.Ptr:85return getSignature(t.Elem(), depth)86case reflect.String:87if t == objectPathType {88return "o"89}90return "s"91case reflect.Struct:92if t == variantType {93return "v"94} else if t == signatureType {95return "g"96}97var s string98for i := 0; i < t.NumField(); i++ {99field := t.Field(i)100if field.PkgPath == "" && field.Tag.Get("dbus") != "-" {101s += getSignature(t.Field(i).Type, depth.EnterStruct())102}103}104if len(s) == 0 {105panic(InvalidTypeError{t})106}107return "(" + s + ")"108case reflect.Array, reflect.Slice:109return "a" + getSignature(t.Elem(), depth.EnterArray())110case reflect.Map:111if !isKeyType(t.Key()) {112panic(InvalidTypeError{t})113}114return "a{" + getSignature(t.Key(), depth.EnterArray().EnterDictEntry()) + getSignature(t.Elem(), depth.EnterArray().EnterDictEntry()) + "}"115case reflect.Interface:116return "v"117}118panic(InvalidTypeError{t})119}
120
121// ParseSignature returns the signature represented by this string, or a
122// SignatureError if the string is not a valid signature.
123func ParseSignature(s string) (sig Signature, err error) {124if len(s) == 0 {125return126}127if len(s) > 255 {128return Signature{""}, SignatureError{s, "too long"}129}130sig.str = s131for err == nil && len(s) != 0 {132err, s = validSingle(s, &depthCounter{})133}134if err != nil {135sig = Signature{""}136}137
138return139}
140
141// ParseSignatureMust behaves like ParseSignature, except that it panics if s
142// is not valid.
143func ParseSignatureMust(s string) Signature {144sig, err := ParseSignature(s)145if err != nil {146panic(err)147}148return sig149}
150
151// Empty returns whether the signature is the empty signature.
152func (s Signature) Empty() bool {153return s.str == ""154}
155
156// Single returns whether the signature represents a single, complete type.
157func (s Signature) Single() bool {158err, r := validSingle(s.str, &depthCounter{})159return err != nil && r == ""160}
161
162// String returns the signature's string representation.
163func (s Signature) String() string {164return s.str165}
166
167// A SignatureError indicates that a signature passed to a function or received
168// on a connection is not a valid signature.
169type SignatureError struct {170Sig string171Reason string172}
173
174func (e SignatureError) Error() string {175return fmt.Sprintf("dbus: invalid signature: %q (%s)", e.Sig, e.Reason)176}
177
178type depthCounter struct {179arrayDepth, structDepth, dictEntryDepth int180}
181
182func (cnt *depthCounter) Valid() bool {183return cnt.arrayDepth <= 32 && cnt.structDepth <= 32 && cnt.dictEntryDepth <= 32184}
185
186func (cnt depthCounter) EnterArray() *depthCounter {187cnt.arrayDepth++188return &cnt189}
190
191func (cnt depthCounter) EnterStruct() *depthCounter {192cnt.structDepth++193return &cnt194}
195
196func (cnt depthCounter) EnterDictEntry() *depthCounter {197cnt.dictEntryDepth++198return &cnt199}
200
201// Try to read a single type from this string. If it was successful, err is nil
202// and rem is the remaining unparsed part. Otherwise, err is a non-nil
203// SignatureError and rem is "". depth is the current recursion depth which may
204// not be greater than 64 and should be given as 0 on the first call.
205func validSingle(s string, depth *depthCounter) (err error, rem string) {206if s == "" {207return SignatureError{Sig: s, Reason: "empty signature"}, ""208}209if !depth.Valid() {210return SignatureError{Sig: s, Reason: "container nesting too deep"}, ""211}212switch s[0] {213case 'y', 'b', 'n', 'q', 'i', 'u', 'x', 't', 'd', 's', 'g', 'o', 'v', 'h':214return nil, s[1:]215case 'a':216if len(s) > 1 && s[1] == '{' {217i := findMatching(s[1:], '{', '}')218if i == -1 {219return SignatureError{Sig: s, Reason: "unmatched '{'"}, ""220}221i++222rem = s[i+1:]223s = s[2:i]224if err, _ = validSingle(s[:1], depth.EnterArray().EnterDictEntry()); err != nil {225return err, ""226}227err, nr := validSingle(s[1:], depth.EnterArray().EnterDictEntry())228if err != nil {229return err, ""230}231if nr != "" {232return SignatureError{Sig: s, Reason: "too many types in dict"}, ""233}234return nil, rem235}236return validSingle(s[1:], depth.EnterArray())237case '(':238i := findMatching(s, '(', ')')239if i == -1 {240return SignatureError{Sig: s, Reason: "unmatched ')'"}, ""241}242rem = s[i+1:]243s = s[1:i]244for err == nil && s != "" {245err, s = validSingle(s, depth.EnterStruct())246}247if err != nil {248rem = ""249}250return251}252return SignatureError{Sig: s, Reason: "invalid type character"}, ""253}
254
255func findMatching(s string, left, right rune) int {256n := 0257for i, v := range s {258if v == left {259n++260} else if v == right {261n--262}263if n == 0 {264return i265}266}267return -1268}
269
270// typeFor returns the type of the given signature. It ignores any left over
271// characters and panics if s doesn't start with a valid type signature.
272func typeFor(s string) (t reflect.Type) {273err, _ := validSingle(s, &depthCounter{})274if err != nil {275panic(err)276}277
278if t, ok := sigToType[s[0]]; ok {279return t280}281switch s[0] {282case 'a':283if s[1] == '{' {284i := strings.LastIndex(s, "}")285t = reflect.MapOf(sigToType[s[2]], typeFor(s[3:i]))286} else {287t = reflect.SliceOf(typeFor(s[1:]))288}289case '(':290t = interfacesType291}292return293}
294