All checks were successful
		
		
	
	Test / Create distribution (push) Successful in 34s
				
			Test / Sandbox (push) Successful in 2m10s
				
			Test / Hakurei (push) Successful in 3m16s
				
			Test / Hpkg (push) Successful in 4m5s
				
			Test / Sandbox (race detector) (push) Successful in 4m30s
				
			Test / Hakurei (race detector) (push) Successful in 5m16s
				
			Test / Flake checks (push) Successful in 1m40s
				
			This is left over from before dbus.Final. Remove them now as they serve no purpose. Signed-off-by: Ophestra <cat@gensokyo.uk>
		
			
				
	
	
		
			194 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			194 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package dbus
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"encoding/hex"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"slices"
 | |
| )
 | |
| 
 | |
| type AddrEntry struct {
 | |
| 	Method string      `json:"method"`
 | |
| 	Values [][2]string `json:"values"`
 | |
| }
 | |
| 
 | |
| // EqualAddrEntries returns whether two slices of [AddrEntry] are equal.
 | |
| func EqualAddrEntries(entries, target []AddrEntry) bool {
 | |
| 	return slices.EqualFunc(entries, target, func(a AddrEntry, b AddrEntry) bool {
 | |
| 		return a.Method == b.Method && slices.Equal(a.Values, b.Values)
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // Parse parses D-Bus address according to
 | |
| // https://dbus.freedesktop.org/doc/dbus-specification.html#addresses
 | |
| func Parse(addr []byte) ([]AddrEntry, error) {
 | |
| 	// Look for a semicolon
 | |
| 	address := bytes.Split(bytes.TrimSuffix(addr, []byte{';'}), []byte{';'})
 | |
| 
 | |
| 	// Allocate for entries
 | |
| 	v := make([]AddrEntry, len(address))
 | |
| 
 | |
| 	for i, s := range address {
 | |
| 		var pairs [][]byte
 | |
| 
 | |
| 		// Look for the colon :
 | |
| 		if method, list, ok := bytes.Cut(s, []byte{':'}); !ok {
 | |
| 			return v, &BadAddressError{ErrNoColon, i, s, -1, nil}
 | |
| 		} else {
 | |
| 			pairs = bytes.Split(list, []byte{','})
 | |
| 			v[i].Method = string(method)
 | |
| 			v[i].Values = make([][2]string, len(pairs))
 | |
| 		}
 | |
| 
 | |
| 		for j, pair := range pairs {
 | |
| 			key, value, ok := bytes.Cut(pair, []byte{'='})
 | |
| 			if !ok {
 | |
| 				return v, &BadAddressError{ErrBadPairSep, i, s, j, pair}
 | |
| 			}
 | |
| 			if len(key) == 0 {
 | |
| 				return v, &BadAddressError{ErrBadPairKey, i, s, j, pair}
 | |
| 			}
 | |
| 			if len(value) == 0 {
 | |
| 				return v, &BadAddressError{ErrBadPairVal, i, s, j, pair}
 | |
| 			}
 | |
| 			v[i].Values[j][0] = string(key)
 | |
| 
 | |
| 			if val, errno := unescapeValue(value); errno != errSuccess {
 | |
| 				return v, &BadAddressError{errno, i, s, j, pair}
 | |
| 			} else {
 | |
| 				v[i].Values[j][1] = string(val)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return v, nil
 | |
| }
 | |
| 
 | |
| func unescapeValue(v []byte) (val []byte, errno ParseError) {
 | |
| 	if l := len(v) - (bytes.Count(v, []byte{'%'}) * 2); l < 0 {
 | |
| 		errno = ErrBadValLength
 | |
| 		return
 | |
| 	} else {
 | |
| 		val = make([]byte, l)
 | |
| 	}
 | |
| 
 | |
| 	var i, skip int
 | |
| 	for iu, b := range v {
 | |
| 		if skip > 0 {
 | |
| 			skip--
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		if ib := bytes.IndexByte([]byte("-_/.\\*"), b); ib != -1 { // - // _/.\*
 | |
| 			goto opt
 | |
| 		} else if b >= '0' && b <= '9' { // 0-9
 | |
| 			goto opt
 | |
| 		} else if b >= 'A' && b <= 'Z' { // A-Z
 | |
| 			goto opt
 | |
| 		} else if b >= 'a' && b <= 'z' { // a-z
 | |
| 			goto opt
 | |
| 		}
 | |
| 
 | |
| 		if b != '%' {
 | |
| 			errno = ErrBadValByte
 | |
| 			break
 | |
| 		}
 | |
| 
 | |
| 		skip += 2
 | |
| 		if iu+2 >= len(v) {
 | |
| 			errno = ErrBadValHexLength
 | |
| 			break
 | |
| 		}
 | |
| 		if c, err := hex.Decode(val[i:i+1], v[iu+1:iu+3]); err != nil {
 | |
| 			if errors.As(err, new(hex.InvalidByteError)) {
 | |
| 				errno = ErrBadValHexByte
 | |
| 				break
 | |
| 			}
 | |
| 			// unreachable
 | |
| 			panic(err.Error())
 | |
| 		} else if c != 1 {
 | |
| 			// unreachable
 | |
| 			panic(fmt.Sprintf("invalid decode length %d", c))
 | |
| 		}
 | |
| 		i++
 | |
| 		continue
 | |
| 
 | |
| 	opt:
 | |
| 		val[i] = b
 | |
| 		i++
 | |
| 	}
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| type ParseError uint8
 | |
| 
 | |
| func (e ParseError) Error() string {
 | |
| 	switch e {
 | |
| 	case errSuccess:
 | |
| 		panic("attempted to return success as error")
 | |
| 	case ErrNoColon:
 | |
| 		return "address does not contain a colon"
 | |
| 	case ErrBadPairSep:
 | |
| 		return "'=' character not found"
 | |
| 	case ErrBadPairKey:
 | |
| 		return "'=' character has no key preceding it"
 | |
| 	case ErrBadPairVal:
 | |
| 		return "'=' character has no value following it"
 | |
| 	case ErrBadValLength:
 | |
| 		return "unescaped value has impossible length"
 | |
| 	case ErrBadValByte:
 | |
| 		return "in D-Bus address, characters other than [-0-9A-Za-z_/.\\*] should have been escaped"
 | |
| 	case ErrBadValHexLength:
 | |
| 		return "in D-Bus address, percent character was not followed by two hex digits"
 | |
| 	case ErrBadValHexByte:
 | |
| 		return "in D-Bus address, percent character was followed by characters other than hex digits"
 | |
| 
 | |
| 	default:
 | |
| 		return fmt.Sprintf("parse error %d", e)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| const (
 | |
| 	errSuccess ParseError = iota
 | |
| 	ErrNoColon
 | |
| 	ErrBadPairSep
 | |
| 	ErrBadPairKey
 | |
| 	ErrBadPairVal
 | |
| 	ErrBadValLength
 | |
| 	ErrBadValByte
 | |
| 	ErrBadValHexLength
 | |
| 	ErrBadValHexByte
 | |
| )
 | |
| 
 | |
| type BadAddressError struct {
 | |
| 	// error type
 | |
| 	Type ParseError
 | |
| 
 | |
| 	// bad entry position
 | |
| 	EntryPos int
 | |
| 	// bad entry value
 | |
| 	EntryVal []byte
 | |
| 
 | |
| 	// bad pair position
 | |
| 	PairPos int
 | |
| 	// bad pair value
 | |
| 	PairVal []byte
 | |
| }
 | |
| 
 | |
| func (a *BadAddressError) Is(err error) bool {
 | |
| 	var b *BadAddressError
 | |
| 	return errors.As(err, &b) && a.Type == b.Type &&
 | |
| 		a.EntryPos == b.EntryPos && slices.Equal(a.EntryVal, b.EntryVal) &&
 | |
| 		a.PairPos == b.PairPos && slices.Equal(a.PairVal, b.PairVal)
 | |
| }
 | |
| 
 | |
| func (a *BadAddressError) Error() string {
 | |
| 	return a.Type.Error()
 | |
| }
 | |
| 
 | |
| func (a *BadAddressError) Unwrap() error {
 | |
| 	return a.Type
 | |
| }
 |