go-tg-screenshot-bot

Форк
0
463 строки · 14.2 Кб
1
package dbus
2

3
import (
4
	"errors"
5
	"fmt"
6
	"os"
7
	"reflect"
8
	"strings"
9
)
10

11
var (
12
	ErrMsgInvalidArg = Error{
13
		"org.freedesktop.DBus.Error.InvalidArgs",
14
		[]interface{}{"Invalid type / number of args"},
15
	}
16
	ErrMsgNoObject = Error{
17
		"org.freedesktop.DBus.Error.NoSuchObject",
18
		[]interface{}{"No such object"},
19
	}
20
	ErrMsgUnknownMethod = Error{
21
		"org.freedesktop.DBus.Error.UnknownMethod",
22
		[]interface{}{"Unknown / invalid method"},
23
	}
24
	ErrMsgUnknownInterface = Error{
25
		"org.freedesktop.DBus.Error.UnknownInterface",
26
		[]interface{}{"Object does not implement the interface"},
27
	}
28
)
29

30
func MakeNoObjectError(path ObjectPath) Error {
31
	return Error{
32
		"org.freedesktop.DBus.Error.NoSuchObject",
33
		[]interface{}{fmt.Sprintf("No such object '%s'", string(path))},
34
	}
35
}
36

37
func MakeUnknownMethodError(methodName string) Error {
38
	return Error{
39
		"org.freedesktop.DBus.Error.UnknownMethod",
40
		[]interface{}{fmt.Sprintf("Unknown / invalid method '%s'", methodName)},
41
	}
42
}
43

44
func MakeUnknownInterfaceError(ifaceName string) Error {
45
	return Error{
46
		"org.freedesktop.DBus.Error.UnknownInterface",
47
		[]interface{}{fmt.Sprintf("Object does not implement the interface '%s'", ifaceName)},
48
	}
49
}
50

51
func MakeFailedError(err error) *Error {
52
	return &Error{
53
		"org.freedesktop.DBus.Error.Failed",
54
		[]interface{}{err.Error()},
55
	}
56
}
57

58
// Sender is a type which can be used in exported methods to receive the message
59
// sender.
60
type Sender string
61

62
func computeMethodName(name string, mapping map[string]string) string {
63
	newname, ok := mapping[name]
64
	if ok {
65
		name = newname
66
	}
67
	return name
68
}
69

70
func getMethods(in interface{}, mapping map[string]string) map[string]reflect.Value {
71
	if in == nil {
72
		return nil
73
	}
74
	methods := make(map[string]reflect.Value)
75
	val := reflect.ValueOf(in)
76
	typ := val.Type()
77
	for i := 0; i < typ.NumMethod(); i++ {
78
		methtype := typ.Method(i)
79
		method := val.Method(i)
80
		t := method.Type()
81
		// only track valid methods must return *Error as last arg
82
		// and must be exported
83
		if t.NumOut() == 0 ||
84
			t.Out(t.NumOut()-1) != reflect.TypeOf(&ErrMsgInvalidArg) ||
85
			methtype.PkgPath != "" {
86
			continue
87
		}
88
		// map names while building table
89
		methods[computeMethodName(methtype.Name, mapping)] = method
90
	}
91
	return methods
92
}
93

94
func getAllMethods(in interface{}, mapping map[string]string) map[string]reflect.Value {
95
	if in == nil {
96
		return nil
97
	}
98
	methods := make(map[string]reflect.Value)
99
	val := reflect.ValueOf(in)
100
	typ := val.Type()
101
	for i := 0; i < typ.NumMethod(); i++ {
102
		methtype := typ.Method(i)
103
		method := val.Method(i)
104
		// map names while building table
105
		methods[computeMethodName(methtype.Name, mapping)] = method
106
	}
107
	return methods
108
}
109

110
func standardMethodArgumentDecode(m Method, sender string, msg *Message, body []interface{}) ([]interface{}, error) {
111
	pointers := make([]interface{}, m.NumArguments())
112
	decode := make([]interface{}, 0, len(body))
113

114
	for i := 0; i < m.NumArguments(); i++ {
115
		tp := reflect.TypeOf(m.ArgumentValue(i))
116
		val := reflect.New(tp)
117
		pointers[i] = val.Interface()
118
		if tp == reflect.TypeOf((*Sender)(nil)).Elem() {
119
			val.Elem().SetString(sender)
120
		} else if tp == reflect.TypeOf((*Message)(nil)).Elem() {
121
			val.Elem().Set(reflect.ValueOf(*msg))
122
		} else {
123
			decode = append(decode, pointers[i])
124
		}
125
	}
126

127
	if len(decode) != len(body) {
128
		return nil, ErrMsgInvalidArg
129
	}
130

131
	if err := Store(body, decode...); err != nil {
132
		return nil, ErrMsgInvalidArg
133
	}
134

135
	return pointers, nil
136
}
137

138
func (conn *Conn) decodeArguments(m Method, sender string, msg *Message) ([]interface{}, error) {
139
	if decoder, ok := m.(ArgumentDecoder); ok {
140
		return decoder.DecodeArguments(conn, sender, msg, msg.Body)
141
	}
142
	return standardMethodArgumentDecode(m, sender, msg, msg.Body)
143
}
144

145
// handleCall handles the given method call (i.e. looks if it's one of the
146
// pre-implemented ones and searches for a corresponding handler if not).
147
func (conn *Conn) handleCall(msg *Message) {
148
	name := msg.Headers[FieldMember].value.(string)
149
	path := msg.Headers[FieldPath].value.(ObjectPath)
150
	ifaceName, _ := msg.Headers[FieldInterface].value.(string)
151
	sender, hasSender := msg.Headers[FieldSender].value.(string)
152
	serial := msg.serial
153

154
	if len(name) == 0 {
155
		conn.sendError(ErrMsgUnknownMethod, sender, serial)
156
	}
157

158
	if ifaceName == "org.freedesktop.DBus.Peer" {
159
		switch name {
160
		case "Ping":
161
			conn.sendReply(sender, serial)
162
		case "GetMachineId":
163
			conn.sendReply(sender, serial, conn.uuid)
164
		default:
165
			conn.sendError(MakeUnknownMethodError(name), sender, serial)
166
		}
167
		return
168
	}
169

170
	object, ok := conn.handler.LookupObject(path)
171
	if !ok {
172
		conn.sendError(MakeNoObjectError(path), sender, serial)
173
		return
174
	}
175

176
	iface, exists := object.LookupInterface(ifaceName)
177
	if !exists {
178
		conn.sendError(MakeUnknownInterfaceError(ifaceName), sender, serial)
179
		return
180
	}
181

182
	m, exists := iface.LookupMethod(name)
183
	if !exists {
184
		conn.sendError(MakeUnknownMethodError(name), sender, serial)
185
		return
186
	}
187
	args, err := conn.decodeArguments(m, sender, msg)
188
	if err != nil {
189
		conn.sendError(err, sender, serial)
190
		return
191
	}
192

193
	ret, err := m.Call(args...)
194
	if err != nil {
195
		conn.sendError(err, sender, serial)
196
		return
197
	}
198

199
	if msg.Flags&FlagNoReplyExpected == 0 {
200
		reply := new(Message)
201
		reply.Type = TypeMethodReply
202
		reply.Headers = make(map[HeaderField]Variant)
203
		if hasSender {
204
			reply.Headers[FieldDestination] = msg.Headers[FieldSender]
205
		}
206
		reply.Headers[FieldReplySerial] = MakeVariant(msg.serial)
207
		reply.Body = make([]interface{}, len(ret))
208
		for i := 0; i < len(ret); i++ {
209
			reply.Body[i] = ret[i]
210
		}
211
		reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...))
212

213
		if err := reply.IsValid(); err != nil {
214
			fmt.Fprintf(os.Stderr, "dbus: dropping invalid reply to %s.%s on obj %s: %s\n", ifaceName, name, path, err)
215
		} else {
216
			conn.sendMessageAndIfClosed(reply, nil)
217
		}
218
	}
219
}
220

221
// Emit emits the given signal on the message bus. The name parameter must be
222
// formatted as "interface.member", e.g., "org.freedesktop.DBus.NameLost".
223
func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) error {
224
	i := strings.LastIndex(name, ".")
225
	if i == -1 {
226
		return errors.New("dbus: invalid method name")
227
	}
228
	iface := name[:i]
229
	member := name[i+1:]
230
	msg := new(Message)
231
	msg.Type = TypeSignal
232
	msg.Headers = make(map[HeaderField]Variant)
233
	msg.Headers[FieldInterface] = MakeVariant(iface)
234
	msg.Headers[FieldMember] = MakeVariant(member)
235
	msg.Headers[FieldPath] = MakeVariant(path)
236
	msg.Body = values
237
	if len(values) > 0 {
238
		msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...))
239
	}
240
	if err := msg.IsValid(); err != nil {
241
		return err
242
	}
243

244
	var closed bool
245
	conn.sendMessageAndIfClosed(msg, func() {
246
		closed = true
247
	})
248
	if closed {
249
		return ErrClosed
250
	}
251
	return nil
252
}
253

254
// Export registers the given value to be exported as an object on the
255
// message bus.
256
//
257
// If a method call on the given path and interface is received, an exported
258
// method with the same name is called with v as the receiver if the
259
// parameters match and the last return value is of type *Error. If this
260
// *Error is not nil, it is sent back to the caller as an error.
261
// Otherwise, a method reply is sent with the other return values as its body.
262
//
263
// Any parameters with the special type Sender are set to the sender of the
264
// dbus message when the method is called. Parameters of this type do not
265
// contribute to the dbus signature of the method (i.e. the method is exposed
266
// as if the parameters of type Sender were not there).
267
//
268
// Similarly, any parameters with the type Message are set to the raw message
269
// received on the bus. Again, parameters of this type do not contribute to the
270
// dbus signature of the method.
271
//
272
// Every method call is executed in a new goroutine, so the method may be called
273
// in multiple goroutines at once.
274
//
275
// Method calls on the interface org.freedesktop.DBus.Peer will be automatically
276
// handled for every object.
277
//
278
// Passing nil as the first parameter will cause conn to cease handling calls on
279
// the given combination of path and interface.
280
//
281
// Export returns an error if path is not a valid path name.
282
func (conn *Conn) Export(v interface{}, path ObjectPath, iface string) error {
283
	return conn.ExportWithMap(v, nil, path, iface)
284
}
285

286
// ExportAll registers all exported methods defined by the given object on
287
// the message bus.
288
//
289
// Unlike Export there is no requirement to have the last parameter as type
290
// *Error. If you want to be able to return error then you can append an error
291
// type parameter to your method signature. If the error returned is not nil,
292
// it is sent back to the caller as an error. Otherwise, a method reply is
293
// sent with the other return values as its body.
294
func (conn *Conn) ExportAll(v interface{}, path ObjectPath, iface string) error {
295
	return conn.export(getAllMethods(v, nil), path, iface, false)
296
}
297

298
// ExportWithMap works exactly like Export but provides the ability to remap
299
// method names (e.g. export a lower-case method).
300
//
301
// The keys in the map are the real method names (exported on the struct), and
302
// the values are the method names to be exported on DBus.
303
func (conn *Conn) ExportWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error {
304
	return conn.export(getMethods(v, mapping), path, iface, false)
305
}
306

307
// ExportSubtree works exactly like Export but registers the given value for
308
// an entire subtree rather under the root path provided.
309
//
310
// In order to make this useful, one parameter in each of the value's exported
311
// methods should be a Message, in which case it will contain the raw message
312
// (allowing one to get access to the path that caused the method to be called).
313
//
314
// Note that more specific export paths take precedence over less specific. For
315
// example, a method call using the ObjectPath /foo/bar/baz will call a method
316
// exported on /foo/bar before a method exported on /foo.
317
func (conn *Conn) ExportSubtree(v interface{}, path ObjectPath, iface string) error {
318
	return conn.ExportSubtreeWithMap(v, nil, path, iface)
319
}
320

321
// ExportSubtreeWithMap works exactly like ExportSubtree but provides the
322
// ability to remap method names (e.g. export a lower-case method).
323
//
324
// The keys in the map are the real method names (exported on the struct), and
325
// the values are the method names to be exported on DBus.
326
func (conn *Conn) ExportSubtreeWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error {
327
	return conn.export(getMethods(v, mapping), path, iface, true)
328
}
329

330
// ExportMethodTable like Export registers the given methods as an object
331
// on the message bus. Unlike Export the it uses a method table to define
332
// the object instead of a native go object.
333
//
334
// The method table is a map from method name to function closure
335
// representing the method. This allows an object exported on the bus to not
336
// necessarily be a native go object. It can be useful for generating exposed
337
// methods on the fly.
338
//
339
// Any non-function objects in the method table are ignored.
340
func (conn *Conn) ExportMethodTable(methods map[string]interface{}, path ObjectPath, iface string) error {
341
	return conn.exportMethodTable(methods, path, iface, false)
342
}
343

344
// Like ExportSubtree, but with the same caveats as ExportMethodTable.
345
func (conn *Conn) ExportSubtreeMethodTable(methods map[string]interface{}, path ObjectPath, iface string) error {
346
	return conn.exportMethodTable(methods, path, iface, true)
347
}
348

349
func (conn *Conn) exportMethodTable(methods map[string]interface{}, path ObjectPath, iface string, includeSubtree bool) error {
350
	var out map[string]reflect.Value
351
	if methods != nil {
352
		out = make(map[string]reflect.Value)
353
		for name, method := range methods {
354
			rval := reflect.ValueOf(method)
355
			if rval.Kind() != reflect.Func {
356
				continue
357
			}
358
			t := rval.Type()
359
			// only track valid methods must return *Error as last arg
360
			if t.NumOut() == 0 ||
361
				t.Out(t.NumOut()-1) != reflect.TypeOf(&ErrMsgInvalidArg) {
362
				continue
363
			}
364
			out[name] = rval
365
		}
366
	}
367
	return conn.export(out, path, iface, includeSubtree)
368
}
369

370
func (conn *Conn) unexport(h *defaultHandler, path ObjectPath, iface string) error {
371
	if h.PathExists(path) {
372
		obj := h.objects[path]
373
		obj.DeleteInterface(iface)
374
		if len(obj.interfaces) == 0 {
375
			h.DeleteObject(path)
376
		}
377
	}
378
	return nil
379
}
380

381
// export is the worker function for all exports/registrations.
382
func (conn *Conn) export(methods map[string]reflect.Value, path ObjectPath, iface string, includeSubtree bool) error {
383
	h, ok := conn.handler.(*defaultHandler)
384
	if !ok {
385
		return fmt.Errorf(
386
			`dbus: export only allowed on the default handler. Received: %T"`,
387
			conn.handler)
388
	}
389

390
	if !path.IsValid() {
391
		return fmt.Errorf(`dbus: Invalid path name: "%s"`, path)
392
	}
393

394
	// Remove a previous export if the interface is nil
395
	if methods == nil {
396
		return conn.unexport(h, path, iface)
397
	}
398

399
	// If this is the first handler for this path, make a new map to hold all
400
	// handlers for this path.
401
	if !h.PathExists(path) {
402
		h.AddObject(path, newExportedObject())
403
	}
404

405
	exportedMethods := make(map[string]Method)
406
	for name, method := range methods {
407
		exportedMethods[name] = exportedMethod{method}
408
	}
409

410
	// Finally, save this handler
411
	obj := h.objects[path]
412
	obj.AddInterface(iface, newExportedIntf(exportedMethods, includeSubtree))
413

414
	return nil
415
}
416

417
// ReleaseName calls org.freedesktop.DBus.ReleaseName and awaits a response.
418
func (conn *Conn) ReleaseName(name string) (ReleaseNameReply, error) {
419
	var r uint32
420
	err := conn.busObj.Call("org.freedesktop.DBus.ReleaseName", 0, name).Store(&r)
421
	if err != nil {
422
		return 0, err
423
	}
424
	return ReleaseNameReply(r), nil
425
}
426

427
// RequestName calls org.freedesktop.DBus.RequestName and awaits a response.
428
func (conn *Conn) RequestName(name string, flags RequestNameFlags) (RequestNameReply, error) {
429
	var r uint32
430
	err := conn.busObj.Call("org.freedesktop.DBus.RequestName", 0, name, flags).Store(&r)
431
	if err != nil {
432
		return 0, err
433
	}
434
	return RequestNameReply(r), nil
435
}
436

437
// ReleaseNameReply is the reply to a ReleaseName call.
438
type ReleaseNameReply uint32
439

440
const (
441
	ReleaseNameReplyReleased ReleaseNameReply = 1 + iota
442
	ReleaseNameReplyNonExistent
443
	ReleaseNameReplyNotOwner
444
)
445

446
// RequestNameFlags represents the possible flags for a RequestName call.
447
type RequestNameFlags uint32
448

449
const (
450
	NameFlagAllowReplacement RequestNameFlags = 1 << iota
451
	NameFlagReplaceExisting
452
	NameFlagDoNotQueue
453
)
454

455
// RequestNameReply is the reply to a RequestName call.
456
type RequestNameReply uint32
457

458
const (
459
	RequestNameReplyPrimaryOwner RequestNameReply = 1 + iota
460
	RequestNameReplyInQueue
461
	RequestNameReplyExists
462
	RequestNameReplyAlreadyOwner
463
)
464

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.