| 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) |
| } |