go-tg-screenshot-bot
342 строки · 7.5 Кб
1package dbus
2
3import (
4"bytes"
5"reflect"
6"strings"
7"sync"
8)
9
10func newIntrospectIntf(h *defaultHandler) *exportedIntf {
11methods := make(map[string]Method)
12methods["Introspect"] = exportedMethod{
13reflect.ValueOf(func(msg Message) (string, *Error) {
14path := msg.Headers[FieldPath].value.(ObjectPath)
15return h.introspectPath(path), nil
16}),
17}
18return newExportedIntf(methods, true)
19}
20
21//NewDefaultHandler returns an instance of the default
22//call handler. This is useful if you want to implement only
23//one of the two handlers but not both.
24//
25// Deprecated: this is the default value, don't use it, it will be unexported.
26func NewDefaultHandler() *defaultHandler {
27h := &defaultHandler{
28objects: make(map[ObjectPath]*exportedObj),
29defaultIntf: make(map[string]*exportedIntf),
30}
31h.defaultIntf["org.freedesktop.DBus.Introspectable"] = newIntrospectIntf(h)
32return h
33}
34
35type defaultHandler struct {
36sync.RWMutex
37objects map[ObjectPath]*exportedObj
38defaultIntf map[string]*exportedIntf
39}
40
41func (h *defaultHandler) PathExists(path ObjectPath) bool {
42_, ok := h.objects[path]
43return ok
44}
45
46func (h *defaultHandler) introspectPath(path ObjectPath) string {
47subpath := make(map[string]struct{})
48var xml bytes.Buffer
49xml.WriteString("<node>")
50for obj := range h.objects {
51p := string(path)
52if p != "/" {
53p += "/"
54}
55if strings.HasPrefix(string(obj), p) {
56node_name := strings.Split(string(obj[len(p):]), "/")[0]
57subpath[node_name] = struct{}{}
58}
59}
60for s := range subpath {
61xml.WriteString("\n\t<node name=\"" + s + "\"/>")
62}
63xml.WriteString("\n</node>")
64return xml.String()
65}
66
67func (h *defaultHandler) LookupObject(path ObjectPath) (ServerObject, bool) {
68h.RLock()
69defer h.RUnlock()
70object, ok := h.objects[path]
71if ok {
72return object, ok
73}
74
75// If an object wasn't found for this exact path,
76// look for a matching subtree registration
77subtreeObject := newExportedObject()
78path = path[:strings.LastIndex(string(path), "/")]
79for len(path) > 0 {
80object, ok = h.objects[path]
81if ok {
82for name, iface := range object.interfaces {
83// Only include this handler if it registered for the subtree
84if iface.isFallbackInterface() {
85subtreeObject.interfaces[name] = iface
86}
87}
88break
89}
90
91path = path[:strings.LastIndex(string(path), "/")]
92}
93
94for name, intf := range h.defaultIntf {
95if _, exists := subtreeObject.interfaces[name]; exists {
96continue
97}
98subtreeObject.interfaces[name] = intf
99}
100
101return subtreeObject, true
102}
103
104func (h *defaultHandler) AddObject(path ObjectPath, object *exportedObj) {
105h.Lock()
106h.objects[path] = object
107h.Unlock()
108}
109
110func (h *defaultHandler) DeleteObject(path ObjectPath) {
111h.Lock()
112delete(h.objects, path)
113h.Unlock()
114}
115
116type exportedMethod struct {
117reflect.Value
118}
119
120func (m exportedMethod) Call(args ...interface{}) ([]interface{}, error) {
121t := m.Type()
122
123params := make([]reflect.Value, len(args))
124for i := 0; i < len(args); i++ {
125params[i] = reflect.ValueOf(args[i]).Elem()
126}
127
128ret := m.Value.Call(params)
129var err error
130nilErr := false // The reflection will find almost-nils, let's only pass back clean ones!
131if t.NumOut() > 0 {
132if e, ok := ret[t.NumOut()-1].Interface().(*Error); ok { // godbus *Error
133nilErr = ret[t.NumOut()-1].IsNil()
134ret = ret[:t.NumOut()-1]
135err = e
136} else if ret[t.NumOut()-1].Type().Implements(errType) { // Go error
137i := ret[t.NumOut()-1].Interface()
138if i == nil {
139nilErr = ret[t.NumOut()-1].IsNil()
140} else {
141err = i.(error)
142}
143ret = ret[:t.NumOut()-1]
144}
145}
146out := make([]interface{}, len(ret))
147for i, val := range ret {
148out[i] = val.Interface()
149}
150if nilErr || err == nil {
151//concrete type to interface nil is a special case
152return out, nil
153}
154return out, err
155}
156
157func (m exportedMethod) NumArguments() int {
158return m.Value.Type().NumIn()
159}
160
161func (m exportedMethod) ArgumentValue(i int) interface{} {
162return reflect.Zero(m.Type().In(i)).Interface()
163}
164
165func (m exportedMethod) NumReturns() int {
166return m.Value.Type().NumOut()
167}
168
169func (m exportedMethod) ReturnValue(i int) interface{} {
170return reflect.Zero(m.Type().Out(i)).Interface()
171}
172
173func newExportedObject() *exportedObj {
174return &exportedObj{
175interfaces: make(map[string]*exportedIntf),
176}
177}
178
179type exportedObj struct {
180mu sync.RWMutex
181interfaces map[string]*exportedIntf
182}
183
184func (obj *exportedObj) LookupInterface(name string) (Interface, bool) {
185if name == "" {
186return obj, true
187}
188obj.mu.RLock()
189defer obj.mu.RUnlock()
190intf, exists := obj.interfaces[name]
191return intf, exists
192}
193
194func (obj *exportedObj) AddInterface(name string, iface *exportedIntf) {
195obj.mu.Lock()
196defer obj.mu.Unlock()
197obj.interfaces[name] = iface
198}
199
200func (obj *exportedObj) DeleteInterface(name string) {
201obj.mu.Lock()
202defer obj.mu.Unlock()
203delete(obj.interfaces, name)
204}
205
206func (obj *exportedObj) LookupMethod(name string) (Method, bool) {
207obj.mu.RLock()
208defer obj.mu.RUnlock()
209for _, intf := range obj.interfaces {
210method, exists := intf.LookupMethod(name)
211if exists {
212return method, exists
213}
214}
215return nil, false
216}
217
218func (obj *exportedObj) isFallbackInterface() bool {
219return false
220}
221
222func newExportedIntf(methods map[string]Method, includeSubtree bool) *exportedIntf {
223return &exportedIntf{
224methods: methods,
225includeSubtree: includeSubtree,
226}
227}
228
229type exportedIntf struct {
230methods map[string]Method
231
232// Whether or not this export is for the entire subtree
233includeSubtree bool
234}
235
236func (obj *exportedIntf) LookupMethod(name string) (Method, bool) {
237out, exists := obj.methods[name]
238return out, exists
239}
240
241func (obj *exportedIntf) isFallbackInterface() bool {
242return obj.includeSubtree
243}
244
245//NewDefaultSignalHandler returns an instance of the default
246//signal handler. This is useful if you want to implement only
247//one of the two handlers but not both.
248//
249// Deprecated: this is the default value, don't use it, it will be unexported.
250func NewDefaultSignalHandler() *defaultSignalHandler {
251return &defaultSignalHandler{}
252}
253
254type defaultSignalHandler struct {
255mu sync.RWMutex
256closed bool
257signals []*signalChannelData
258}
259
260func (sh *defaultSignalHandler) DeliverSignal(intf, name string, signal *Signal) {
261sh.mu.RLock()
262defer sh.mu.RUnlock()
263if sh.closed {
264return
265}
266for _, scd := range sh.signals {
267scd.deliver(signal)
268}
269}
270
271func (sh *defaultSignalHandler) Terminate() {
272sh.mu.Lock()
273defer sh.mu.Unlock()
274if sh.closed {
275return
276}
277
278for _, scd := range sh.signals {
279scd.close()
280close(scd.ch)
281}
282sh.closed = true
283sh.signals = nil
284}
285
286func (sh *defaultSignalHandler) AddSignal(ch chan<- *Signal) {
287sh.mu.Lock()
288defer sh.mu.Unlock()
289if sh.closed {
290return
291}
292sh.signals = append(sh.signals, &signalChannelData{
293ch: ch,
294done: make(chan struct{}),
295})
296}
297
298func (sh *defaultSignalHandler) RemoveSignal(ch chan<- *Signal) {
299sh.mu.Lock()
300defer sh.mu.Unlock()
301if sh.closed {
302return
303}
304for i := len(sh.signals) - 1; i >= 0; i-- {
305if ch == sh.signals[i].ch {
306sh.signals[i].close()
307copy(sh.signals[i:], sh.signals[i+1:])
308sh.signals[len(sh.signals)-1] = nil
309sh.signals = sh.signals[:len(sh.signals)-1]
310}
311}
312}
313
314type signalChannelData struct {
315wg sync.WaitGroup
316ch chan<- *Signal
317done chan struct{}
318}
319
320func (scd *signalChannelData) deliver(signal *Signal) {
321select {
322case scd.ch <- signal:
323case <-scd.done:
324return
325default:
326scd.wg.Add(1)
327go scd.deferredDeliver(signal)
328}
329}
330
331func (scd *signalChannelData) deferredDeliver(signal *Signal) {
332select {
333case scd.ch <- signal:
334case <-scd.done:
335}
336scd.wg.Done()
337}
338
339func (scd *signalChannelData) close() {
340close(scd.done)
341scd.wg.Wait() // wait until all spawned goroutines return
342}
343