1package filexfer
2
3// ClosePacket defines the SSH_FXP_CLOSE packet.
4type ClosePacket struct {
5	Handle string
6}
7
8// Type returns the SSH_FXP_xy value associated with this packet type.
9func (p *ClosePacket) Type() PacketType {
10	return PacketTypeClose
11}
12
13// MarshalPacket returns p as a two-part binary encoding of p.
14func (p *ClosePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
15	buf := NewBuffer(b)
16	if buf.Cap() < 9 {
17		size := 4 + len(p.Handle) // string(handle)
18		buf = NewMarshalBuffer(size)
19	}
20
21	buf.StartPacket(PacketTypeClose, reqid)
22	buf.AppendString(p.Handle)
23
24	return buf.Packet(payload)
25}
26
27// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
28// It is assumed that the uint32(request-id) has already been consumed.
29func (p *ClosePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
30	if p.Handle, err = buf.ConsumeString(); err != nil {
31		return err
32	}
33
34	return nil
35}
36
37// ReadPacket defines the SSH_FXP_READ packet.
38type ReadPacket struct {
39	Handle string
40	Offset uint64
41	Len    uint32
42}
43
44// Type returns the SSH_FXP_xy value associated with this packet type.
45func (p *ReadPacket) Type() PacketType {
46	return PacketTypeRead
47}
48
49// MarshalPacket returns p as a two-part binary encoding of p.
50func (p *ReadPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
51	buf := NewBuffer(b)
52	if buf.Cap() < 9 {
53		// string(handle) + uint64(offset) + uint32(len)
54		size := 4 + len(p.Handle) + 8 + 4
55		buf = NewMarshalBuffer(size)
56	}
57
58	buf.StartPacket(PacketTypeRead, reqid)
59	buf.AppendString(p.Handle)
60	buf.AppendUint64(p.Offset)
61	buf.AppendUint32(p.Len)
62
63	return buf.Packet(payload)
64}
65
66// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
67// It is assumed that the uint32(request-id) has already been consumed.
68func (p *ReadPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
69	if p.Handle, err = buf.ConsumeString(); err != nil {
70		return err
71	}
72
73	if p.Offset, err = buf.ConsumeUint64(); err != nil {
74		return err
75	}
76
77	if p.Len, err = buf.ConsumeUint32(); err != nil {
78		return err
79	}
80
81	return nil
82}
83
84// WritePacket defines the SSH_FXP_WRITE packet.
85type WritePacket struct {
86	Handle string
87	Offset uint64
88	Data   []byte
89}
90
91// Type returns the SSH_FXP_xy value associated with this packet type.
92func (p *WritePacket) Type() PacketType {
93	return PacketTypeWrite
94}
95
96// MarshalPacket returns p as a two-part binary encoding of p.
97func (p *WritePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
98	buf := NewBuffer(b)
99	if buf.Cap() < 9 {
100		// string(handle) + uint64(offset) + uint32(len(data)); data content in payload
101		size := 4 + len(p.Handle) + 8 + 4
102		buf = NewMarshalBuffer(size)
103	}
104
105	buf.StartPacket(PacketTypeWrite, reqid)
106	buf.AppendString(p.Handle)
107	buf.AppendUint64(p.Offset)
108	buf.AppendUint32(uint32(len(p.Data)))
109
110	return buf.Packet(p.Data)
111}
112
113// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
114// It is assumed that the uint32(request-id) has already been consumed.
115//
116// If p.Data is already populated, and of sufficient length to hold the data,
117// then this will copy the data into that byte slice.
118//
119// If p.Data has a length insufficient to hold the data,
120// then this will make a new slice of sufficient length, and copy the data into that.
121//
122// This means this _does not_ alias any of the data buffer that is passed in.
123func (p *WritePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
124	if p.Handle, err = buf.ConsumeString(); err != nil {
125		return err
126	}
127
128	if p.Offset, err = buf.ConsumeUint64(); err != nil {
129		return err
130	}
131
132	data, err := buf.ConsumeByteSlice()
133	if err != nil {
134		return err
135	}
136
137	if len(p.Data) < len(data) {
138		p.Data = make([]byte, len(data))
139	}
140
141	n := copy(p.Data, data)
142	p.Data = p.Data[:n]
143	return nil
144}
145
146// FStatPacket defines the SSH_FXP_FSTAT packet.
147type FStatPacket struct {
148	Handle string
149}
150
151// Type returns the SSH_FXP_xy value associated with this packet type.
152func (p *FStatPacket) Type() PacketType {
153	return PacketTypeFStat
154}
155
156// MarshalPacket returns p as a two-part binary encoding of p.
157func (p *FStatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
158	buf := NewBuffer(b)
159	if buf.Cap() < 9 {
160		size := 4 + len(p.Handle) // string(handle)
161		buf = NewMarshalBuffer(size)
162	}
163
164	buf.StartPacket(PacketTypeFStat, reqid)
165	buf.AppendString(p.Handle)
166
167	return buf.Packet(payload)
168}
169
170// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
171// It is assumed that the uint32(request-id) has already been consumed.
172func (p *FStatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
173	if p.Handle, err = buf.ConsumeString(); err != nil {
174		return err
175	}
176
177	return nil
178}
179
180// FSetstatPacket defines the SSH_FXP_FSETSTAT packet.
181type FSetstatPacket struct {
182	Handle string
183	Attrs  Attributes
184}
185
186// Type returns the SSH_FXP_xy value associated with this packet type.
187func (p *FSetstatPacket) Type() PacketType {
188	return PacketTypeFSetstat
189}
190
191// MarshalPacket returns p as a two-part binary encoding of p.
192func (p *FSetstatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
193	buf := NewBuffer(b)
194	if buf.Cap() < 9 {
195		size := 4 + len(p.Handle) + p.Attrs.Len() // string(handle) + ATTRS(attrs)
196		buf = NewMarshalBuffer(size)
197	}
198
199	buf.StartPacket(PacketTypeFSetstat, reqid)
200	buf.AppendString(p.Handle)
201
202	p.Attrs.MarshalInto(buf)
203
204	return buf.Packet(payload)
205}
206
207// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
208// It is assumed that the uint32(request-id) has already been consumed.
209func (p *FSetstatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
210	if p.Handle, err = buf.ConsumeString(); err != nil {
211		return err
212	}
213
214	return p.Attrs.UnmarshalFrom(buf)
215}
216
217// ReadDirPacket defines the SSH_FXP_READDIR packet.
218type ReadDirPacket struct {
219	Handle string
220}
221
222// Type returns the SSH_FXP_xy value associated with this packet type.
223func (p *ReadDirPacket) Type() PacketType {
224	return PacketTypeReadDir
225}
226
227// MarshalPacket returns p as a two-part binary encoding of p.
228func (p *ReadDirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
229	buf := NewBuffer(b)
230	if buf.Cap() < 9 {
231		size := 4 + len(p.Handle) // string(handle)
232		buf = NewMarshalBuffer(size)
233	}
234
235	buf.StartPacket(PacketTypeReadDir, reqid)
236	buf.AppendString(p.Handle)
237
238	return buf.Packet(payload)
239}
240
241// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
242// It is assumed that the uint32(request-id) has already been consumed.
243func (p *ReadDirPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
244	if p.Handle, err = buf.ConsumeString(); err != nil {
245		return err
246	}
247
248	return nil
249}
250