1package ports
2
3import (
4 "encoding/binary"
5 "errors"
6 "math"
7)
8
9// ErrTooManyCommandArgs reports that a request exceeds the wire count limit.
10var ErrTooManyCommandArgs = errors.New("ports: too many command arguments")
11
12// CommandRequest asks the daemon to run one control command. Version must
13// stay first so a future payload layout can still be rejected cleanly.
14type CommandRequest struct {
15 Version uint16
16 RequestID uint64
17 Attached bool
18 Self bool
19 Slug string
20 Args []string
21 TargetSession string
22 TargetTab string
23 TargetPane string
24 JSON bool
25}
26
27// CommandResult reports a control command's outcome.
28type CommandResult struct {
29 RequestID uint64
30 OK bool
31 Code uint16
32 Text string
33 Output string
34}
35
36// PeekCommandVersion returns the leading protocol version from a
37// CommandRequest payload.
38func PeekCommandVersion(b []byte) (uint16, bool) {
39 if len(b) < 2 {
40 return 0, false
41 }
42 return binary.BigEndian.Uint16(b), true
43}
44
45// MarshalCommandRequest encodes m into a CommandRequest payload.
46func MarshalCommandRequest(m CommandRequest) ([]byte, error) {
47 if len(m.Args) > math.MaxUint16 {
48 return nil, ErrTooManyCommandArgs
49 }
50
51 w := payloadWriter{}
52 w.putUint16(m.Version)
53 w.putUint64(m.RequestID)
54 w.putBool(m.Attached)
55 w.putBool(m.Self)
56 w.putString(m.Slug)
57 w.putUint16(uint16(len(m.Args)))
58 for _, arg := range m.Args {
59 w.putLongString(arg)
60 }
61 w.putString(m.TargetSession)
62 w.putString(m.TargetTab)
63 w.putString(m.TargetPane)
64 w.putBool(m.JSON)
65 return w.b, nil
66}
67
68// UnmarshalCommandRequest decodes a strict CommandRequest payload.
69func UnmarshalCommandRequest(b []byte) (CommandRequest, error) {
70 r := payloadReader{b: b}
71 var m CommandRequest
72 var err error
73
74 if m.Version, err = r.getUint16(); err != nil {
75 return CommandRequest{}, err
76 }
77 if m.RequestID, err = r.getUint64(); err != nil {
78 return CommandRequest{}, err
79 }
80 if m.Attached, err = r.getBool(); err != nil {
81 return CommandRequest{}, err
82 }
83 if m.Self, err = r.getBool(); err != nil {
84 return CommandRequest{}, err
85 }
86 if m.Slug, err = r.getString(); err != nil {
87 return CommandRequest{}, err
88 }
89 argCount, err := r.getUint16()
90 if err != nil {
91 return CommandRequest{}, err
92 }
93 // Every argument requires at least a uint32 length prefix. Reject an
94 // impossible count before allocating from untrusted input.
95 if uint64(argCount) > uint64(len(r.b)/4) {
96 return CommandRequest{}, errShortPayload
97 }
98 if argCount != 0 {
99 m.Args = make([]string, 0, int(argCount))
100 for range int(argCount) {
101 arg, err := r.getLongString()
102 if err != nil {
103 return CommandRequest{}, err
104 }
105 m.Args = append(m.Args, arg)
106 }
107 }
108 if m.TargetSession, err = r.getString(); err != nil {
109 return CommandRequest{}, err
110 }
111 if m.TargetTab, err = r.getString(); err != nil {
112 return CommandRequest{}, err
113 }
114 if m.TargetPane, err = r.getString(); err != nil {
115 return CommandRequest{}, err
116 }
117 if m.JSON, err = r.getBool(); err != nil {
118 return CommandRequest{}, err
119 }
120 if err := r.done(); err != nil {
121 return CommandRequest{}, err
122 }
123 return m, nil
124}
125
126// MarshalCommandResult encodes m into a CommandResult payload.
127func MarshalCommandResult(m CommandResult) []byte {
128 w := payloadWriter{}
129 w.putUint64(m.RequestID)
130 w.putBool(m.OK)
131 w.putUint16(m.Code)
132 w.putString(m.Text)
133 w.putLongString(m.Output)
134 return w.b
135}
136
137// UnmarshalCommandResult decodes a strict CommandResult payload.
138func UnmarshalCommandResult(b []byte) (CommandResult, error) {
139 r := payloadReader{b: b}
140 var m CommandResult
141 var err error
142
143 if m.RequestID, err = r.getUint64(); err != nil {
144 return CommandResult{}, err
145 }
146 if m.OK, err = r.getBool(); err != nil {
147 return CommandResult{}, err
148 }
149 if m.Code, err = r.getUint16(); err != nil {
150 return CommandResult{}, err
151 }
152 if m.Text, err = r.getString(); err != nil {
153 return CommandResult{}, err
154 }
155 if m.Output, err = r.getLongString(); err != nil {
156 return CommandResult{}, err
157 }
158 if err := r.done(); err != nil {
159 return CommandResult{}, err
160 }
161 return m, nil
162}