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