243 lines
6.3 KiB
Go
243 lines
6.3 KiB
Go
package filexfer
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
// StatusPacket defines the SSH_FXP_STATUS packet.
|
|
//
|
|
// Specified in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7
|
|
type StatusPacket struct {
|
|
StatusCode Status
|
|
ErrorMessage string
|
|
LanguageTag string
|
|
}
|
|
|
|
// Error makes StatusPacket an error type.
|
|
func (p *StatusPacket) Error() string {
|
|
if p.ErrorMessage == "" {
|
|
return "sftp: " + p.StatusCode.String()
|
|
}
|
|
|
|
return fmt.Sprintf("sftp: %q (%s)", p.ErrorMessage, p.StatusCode)
|
|
}
|
|
|
|
// Is returns true if target is a StatusPacket with the same StatusCode,
|
|
// or target is a Status code which is the same as SatusCode.
|
|
func (p *StatusPacket) Is(target error) bool {
|
|
if target, ok := target.(*StatusPacket); ok {
|
|
return p.StatusCode == target.StatusCode
|
|
}
|
|
|
|
return p.StatusCode == target
|
|
}
|
|
|
|
// Type returns the SSH_FXP_xy value associated with this packet type.
|
|
func (p *StatusPacket) Type() PacketType {
|
|
return PacketTypeStatus
|
|
}
|
|
|
|
// MarshalPacket returns p as a two-part binary encoding of p.
|
|
func (p *StatusPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
|
buf := NewBuffer(b)
|
|
if buf.Cap() < 9 {
|
|
// uint32(error/status code) + string(error message) + string(language tag)
|
|
size := 4 + 4 + len(p.ErrorMessage) + 4 + len(p.LanguageTag)
|
|
buf = NewMarshalBuffer(size)
|
|
}
|
|
|
|
buf.StartPacket(PacketTypeStatus, reqid)
|
|
buf.AppendUint32(uint32(p.StatusCode))
|
|
buf.AppendString(p.ErrorMessage)
|
|
buf.AppendString(p.LanguageTag)
|
|
|
|
return buf.Packet(payload)
|
|
}
|
|
|
|
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
|
// It is assumed that the uint32(request-id) has already been consumed.
|
|
func (p *StatusPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
|
statusCode, err := buf.ConsumeUint32()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.StatusCode = Status(statusCode)
|
|
|
|
if p.ErrorMessage, err = buf.ConsumeString(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if p.LanguageTag, err = buf.ConsumeString(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// HandlePacket defines the SSH_FXP_HANDLE packet.
|
|
type HandlePacket struct {
|
|
Handle string
|
|
}
|
|
|
|
// Type returns the SSH_FXP_xy value associated with this packet type.
|
|
func (p *HandlePacket) Type() PacketType {
|
|
return PacketTypeHandle
|
|
}
|
|
|
|
// MarshalPacket returns p as a two-part binary encoding of p.
|
|
func (p *HandlePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
|
buf := NewBuffer(b)
|
|
if buf.Cap() < 9 {
|
|
size := 4 + len(p.Handle) // string(handle)
|
|
buf = NewMarshalBuffer(size)
|
|
}
|
|
|
|
buf.StartPacket(PacketTypeHandle, reqid)
|
|
buf.AppendString(p.Handle)
|
|
|
|
return buf.Packet(payload)
|
|
}
|
|
|
|
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
|
// It is assumed that the uint32(request-id) has already been consumed.
|
|
func (p *HandlePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
|
if p.Handle, err = buf.ConsumeString(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// DataPacket defines the SSH_FXP_DATA packet.
|
|
type DataPacket struct {
|
|
Data []byte
|
|
}
|
|
|
|
// Type returns the SSH_FXP_xy value associated with this packet type.
|
|
func (p *DataPacket) Type() PacketType {
|
|
return PacketTypeData
|
|
}
|
|
|
|
// MarshalPacket returns p as a two-part binary encoding of p.
|
|
func (p *DataPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
|
buf := NewBuffer(b)
|
|
if buf.Cap() < 9 {
|
|
size := 4 // uint32(len(data)); data content in payload
|
|
buf = NewMarshalBuffer(size)
|
|
}
|
|
|
|
buf.StartPacket(PacketTypeData, reqid)
|
|
buf.AppendUint32(uint32(len(p.Data)))
|
|
|
|
return buf.Packet(p.Data)
|
|
}
|
|
|
|
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
|
// It is assumed that the uint32(request-id) has already been consumed.
|
|
//
|
|
// If p.Data is already populated, and of sufficient length to hold the data,
|
|
// then this will copy the data into that byte slice.
|
|
//
|
|
// If p.Data has a length insufficient to hold the data,
|
|
// then this will make a new slice of sufficient length, and copy the data into that.
|
|
//
|
|
// This means this _does not_ alias any of the data buffer that is passed in.
|
|
func (p *DataPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
|
data, err := buf.ConsumeByteSlice()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(p.Data) < len(data) {
|
|
p.Data = make([]byte, len(data))
|
|
}
|
|
|
|
n := copy(p.Data, data)
|
|
p.Data = p.Data[:n]
|
|
return nil
|
|
}
|
|
|
|
// NamePacket defines the SSH_FXP_NAME packet.
|
|
type NamePacket struct {
|
|
Entries []*NameEntry
|
|
}
|
|
|
|
// Type returns the SSH_FXP_xy value associated with this packet type.
|
|
func (p *NamePacket) Type() PacketType {
|
|
return PacketTypeName
|
|
}
|
|
|
|
// MarshalPacket returns p as a two-part binary encoding of p.
|
|
func (p *NamePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
|
buf := NewBuffer(b)
|
|
if buf.Cap() < 9 {
|
|
size := 4 // uint32(len(entries))
|
|
|
|
for _, e := range p.Entries {
|
|
size += e.Len()
|
|
}
|
|
|
|
buf = NewMarshalBuffer(size)
|
|
}
|
|
|
|
buf.StartPacket(PacketTypeName, reqid)
|
|
buf.AppendUint32(uint32(len(p.Entries)))
|
|
|
|
for _, e := range p.Entries {
|
|
e.MarshalInto(buf)
|
|
}
|
|
|
|
return buf.Packet(payload)
|
|
}
|
|
|
|
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
|
// It is assumed that the uint32(request-id) has already been consumed.
|
|
func (p *NamePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
|
count, err := buf.ConsumeUint32()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
p.Entries = make([]*NameEntry, 0, count)
|
|
|
|
for i := uint32(0); i < count; i++ {
|
|
var e NameEntry
|
|
if err := e.UnmarshalFrom(buf); err != nil {
|
|
return err
|
|
}
|
|
|
|
p.Entries = append(p.Entries, &e)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// AttrsPacket defines the SSH_FXP_ATTRS packet.
|
|
type AttrsPacket struct {
|
|
Attrs Attributes
|
|
}
|
|
|
|
// Type returns the SSH_FXP_xy value associated with this packet type.
|
|
func (p *AttrsPacket) Type() PacketType {
|
|
return PacketTypeAttrs
|
|
}
|
|
|
|
// MarshalPacket returns p as a two-part binary encoding of p.
|
|
func (p *AttrsPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
|
buf := NewBuffer(b)
|
|
if buf.Cap() < 9 {
|
|
size := p.Attrs.Len() // ATTRS(attrs)
|
|
buf = NewMarshalBuffer(size)
|
|
}
|
|
|
|
buf.StartPacket(PacketTypeAttrs, reqid)
|
|
p.Attrs.MarshalInto(buf)
|
|
|
|
return buf.Packet(payload)
|
|
}
|
|
|
|
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
|
|
// It is assumed that the uint32(request-id) has already been consumed.
|
|
func (p *AttrsPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
|
|
return p.Attrs.UnmarshalFrom(buf)
|
|
}
|