v0.1.0 2c367cd

message.go RawHistoryFolder

1package zerobus
2
3import "encoding/binary"
4
5// Type is the kind of a message.
6type Type byte
7
8// Message types.
9const (
10	TypeMethodCall   Type = 1
11	TypeMethodReturn Type = 2
12	TypeError        Type = 3
13	TypeSignal       Type = 4
14)
15
16// Flags are the header flags of a message.
17type Flags byte
18
19// Message flags.
20const (
21	// FlagNoReplyExpected tells the receiver not to answer a method call.
22	FlagNoReplyExpected Flags = 0x1
23	// FlagNoAutoStart tells the bus not to start the destination service.
24	FlagNoAutoStart Flags = 0x2
25	// FlagAllowInteractiveAuth allows the receiver to prompt the user.
26	FlagAllowInteractiveAuth Flags = 0x4
27)
28
29// Header field codes.
30const (
31	fieldPath        = 1
32	fieldInterface   = 2
33	fieldMember      = 3
34	fieldErrorName   = 4
35	fieldReplySerial = 5
36	fieldDestination = 6
37	fieldSender      = 7
38	fieldSignature   = 8
39	fieldUnixFDs     = 9
40)
41
42const headerLen = 16 // the fixed part, up to the length of the field array
43
44// Message is a received message.
45//
46// Its strings point into the connection's receive buffer: they and the
47// message itself are valid only until the next read on the connection.
48type Message struct {
49	Type        Type
50	Flags       Flags
51	Serial      uint32
52	ReplySerial uint32
53	Path        string
54	Interface   string
55	Member      string
56	ErrorName   string
57	Destination string
58	Sender      string
59	Signature   string // the signature of the body
60
61	body []byte
62	big  bool
63	r    Reader
64}
65
66// Body returns a reader at the start of the message body. Each call starts
67// over from the beginning.
68func (m *Message) Body() *Reader {
69	m.r = Reader{buf: m.body, big: m.big}
70	return &m.r
71}
72
73// frameLen returns the full length of the message whose fixed header is b,
74// which must hold at least headerLen bytes.
75func frameLen(b []byte) (int, error) {
76	var order binary.ByteOrder
77	switch b[0] {
78	case 'l':
79		order = binary.LittleEndian
80	case 'B':
81		order = binary.BigEndian
82	default:
83		return 0, ErrMalformed
84	}
85	if b[3] != 1 {
86		return 0, ErrMalformed // protocol version
87	}
88	body := uint64(order.Uint32(b[4:]))
89	fields := uint64(order.Uint32(b[12:]))
90	if body > maxMessage || fields > maxArray {
91		return 0, ErrTooLarge
92	}
93	n := uint64(pad(headerLen+int(fields), 8)) + body
94	if n > maxMessage {
95		return 0, ErrTooLarge
96	}
97	return int(n), nil
98}
99
100// parse decodes the message b, whose length frameLen returned, into m. The
101// strings of m point into b.
102func parse(b []byte, m *Message) error {
103	*m = Message{big: b[0] == 'B', Type: Type(b[1]), Flags: Flags(b[2])}
104	r := Reader{buf: b, big: m.big, off: 4}
105	bodyLen := int(r.Uint32())
106	m.Serial = r.Uint32()
107	if m.Serial == 0 {
108		return ErrMalformed
109	}
110	var fds uint32
111	end := r.Array('(')
112	for r.More(end) {
113		r.Struct()
114		code := r.Byte()
115		sig := r.Variant()
116		if r.err != nil {
117			break
118		}
119		want := byte('s')
120		var dst *string
121		switch code {
122		case fieldPath:
123			want, dst = 'o', &m.Path
124		case fieldInterface:
125			dst = &m.Interface
126		case fieldMember:
127			dst = &m.Member
128		case fieldErrorName:
129			dst = &m.ErrorName
130		case fieldDestination:
131			dst = &m.Destination
132		case fieldSender:
133			dst = &m.Sender
134		case fieldSignature:
135			want, dst = 'g', &m.Signature
136		case fieldReplySerial, fieldUnixFDs:
137			if sig != "u" {
138				return ErrMalformed
139			}
140			if code == fieldReplySerial {
141				m.ReplySerial = r.Uint32()
142			} else {
143				fds = r.Uint32()
144			}
145			continue
146		default:
147			r.Skip(sig) // unknown fields must be ignored
148			continue
149		}
150		if len(sig) != 1 || sig[0] != want {
151			return ErrMalformed
152		}
153		if want == 'g' {
154			*dst = r.Signature()
155		} else {
156			*dst = r.Str()
157		}
158	}
159	if r.err != nil {
160		return r.err
161	}
162	if fds != 0 {
163		return ErrMalformed // fd passing is not negotiated, so none may come
164	}
165	start := pad(r.off, 8)
166	if start+bodyLen != len(b) || start > len(b) {
167		return ErrMalformed
168	}
169	for _, c := range b[r.off:start] {
170		if c != 0 {
171			return ErrMalformed
172		}
173	}
174	m.body = b[start:]
175	// No signature means an empty body.
176	if m.Signature == "" && bodyLen != 0 || !validSignature(m.Signature) || m.Path != "" && !validPath(m.Path) {
177		return ErrMalformed
178	}
179	switch m.Type {
180	case TypeMethodCall:
181		if m.Path == "" || m.Member == "" {
182			return ErrMalformed
183		}
184	case TypeSignal:
185		if m.Path == "" || m.Interface == "" || m.Member == "" {
186			return ErrMalformed
187		}
188	case TypeError:
189		if m.ErrorName == "" || m.ReplySerial == 0 {
190			return ErrMalformed
191		}
192	case TypeMethodReturn:
193		if m.ReplySerial == 0 {
194			return ErrMalformed
195		}
196	}
197	return nil
198}
199
200// begin starts a new message in e: the fixed header and the field array, up
201// to the body. Empty strings are left out of the header.
202func (e *Encoder) begin(t Type, serial, replySerial uint32, dest, path, iface, member, errName, sig string) {
203	e.buf = append(e.buf[:0], 'l', byte(t), 0, 1, 0, 0, 0, 0)
204	e.buf = binary.LittleEndian.AppendUint32(e.buf, serial)
205	e.body, e.sig, e.err = 0, sig, nil
206	// A bus disconnects a client that sends an invalid name or leaves out a
207	// field its message type requires.
208	if dest != "" && !validBusName(dest) || iface != "" && !validInterface(iface) ||
209		member != "" && !validMember(member) || errName != "" && !validInterface(errName) {
210		e.invalid()
211	}
212	switch t {
213	case TypeMethodCall:
214		if path == "" || member == "" {
215			e.invalid()
216		}
217	case TypeSignal:
218		if path == "" || iface == "" || member == "" {
219			e.invalid()
220		}
221	case TypeError:
222		if errName == "" {
223			e.invalid()
224		}
225	}
226	fields := e.BeginArray('(')
227	str := func(code byte, typ, v string) {
228		if v == "" {
229			return
230		}
231		e.Struct()
232		e.Byte(code)
233		e.Signature(typ)
234		switch typ {
235		case "o":
236			e.ObjectPath(v)
237		case "g":
238			e.Signature(v)
239		default:
240			e.Str(v)
241		}
242	}
243	str(fieldPath, "o", path)
244	str(fieldInterface, "s", iface)
245	str(fieldMember, "s", member)
246	str(fieldErrorName, "s", errName)
247	if replySerial != 0 {
248		e.Struct()
249		e.Byte(fieldReplySerial)
250		e.Signature("u")
251		e.Uint32(replySerial)
252	}
253	str(fieldDestination, "s", dest)
254	str(fieldSignature, "g", sig)
255	e.EndArray(fields)
256	e.pad(8)
257	e.body = len(e.buf)
258}
259
260// finish writes the body length and checks the body against the signature.
261// It returns the bytes to send.
262func (e *Encoder) finish() ([]byte, error) {
263	if e.body == 0 {
264		return nil, ErrInvalid
265	}
266	if e.err != nil {
267		return nil, e.err
268	}
269	if len(e.buf) > maxMessage {
270		return nil, ErrTooLarge
271	}
272	binary.LittleEndian.PutUint32(e.buf[4:], uint32(len(e.buf)-e.body))
273	// Reading the whole body back with the signature proves it matches: a
274	// bus disconnects a client that sends a body that does not.
275	r := Reader{buf: e.buf[e.body:]}
276	r.check(e.sig)
277	if !r.Done() {
278		return nil, ErrInvalid
279	}
280	return e.buf, nil
281}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 3

↑↓ moveenter openesc close