add helper methods for request flags/attrs data
diff --git a/request-attrs.go b/request-attrs.go
new file mode 100644
index 0000000..0149d93
--- /dev/null
+++ b/request-attrs.go
@@ -0,0 +1,63 @@
+package sftp
+
+// Methods on the Request object to make working with the Flags bitmasks and
+// Attr(ibutes) byte blob easier. Use Pflags() when working with an Open/Write
+// request and AttrFlags() and Attributes() when working with SetStat requests.
+
+import "os"
+
+// Open packet pflags
+type pflags struct {
+ Read, Write, Append, Creat, Trunc, Excl bool
+}
+
+// testable constructor
+func newPflags(flags uint32) pflags {
+ return pflags{
+ Read: flags&ssh_FXF_READ != 0,
+ Write: flags&ssh_FXF_WRITE != 0,
+ Append: flags&ssh_FXF_APPEND != 0,
+ Creat: flags&ssh_FXF_CREAT != 0,
+ Trunc: flags&ssh_FXF_TRUNC != 0,
+ Excl: flags&ssh_FXF_EXCL != 0,
+ }
+}
+
+// Check bitmap/uint32 for Open packet pflag values
+func (r *Request) Pflags() pflags {
+ return newPflags(r.Flags)
+}
+
+// File attribute flags
+type aflags struct {
+ Size, UidGid, Permissions, Acmodtime bool
+}
+
+// testable constructor
+func newAflags(flags uint32) aflags {
+ return aflags{
+ Size: (flags & ssh_FILEXFER_ATTR_SIZE) != 0,
+ UidGid: (flags & ssh_FILEXFER_ATTR_UIDGID) != 0,
+ Permissions: (flags & ssh_FILEXFER_ATTR_PERMISSIONS) != 0,
+ Acmodtime: (flags & ssh_FILEXFER_ATTR_ACMODTIME) != 0,
+ }
+}
+
+// Check bitmap/uint32 for file attribute flags
+func (r *Request) AttrFlags(flags uint32) aflags {
+ return newAflags(r.Flags)
+}
+
+// File attributes
+type fileattrs FileStat
+
+// Return Mode wrapped in os.FileMode
+func (a fileattrs) FileMode() os.FileMode {
+ return os.FileMode(a.Mode)
+}
+
+// Parse file attributes byte blob and return them in object
+func (r *Request) Attributes() fileattrs {
+ fa, _ := getFileStat(r.Flags, r.Attrs)
+ return fileattrs(*fa)
+}
diff --git a/request-attrs_test.go b/request-attrs_test.go
new file mode 100644
index 0000000..bcbb06a
--- /dev/null
+++ b/request-attrs_test.go
@@ -0,0 +1,51 @@
+package sftp
+
+import (
+ "os"
+
+ "github.com/stretchr/testify/assert"
+
+ "testing"
+)
+
+func TestRequestPflags(t *testing.T) {
+ pflags := newPflags(ssh_FXF_READ | ssh_FXF_WRITE | ssh_FXF_APPEND)
+ assert.True(t, pflags.Read)
+ assert.True(t, pflags.Write)
+ assert.True(t, pflags.Append)
+ assert.False(t, pflags.Creat)
+ assert.False(t, pflags.Trunc)
+ assert.False(t, pflags.Excl)
+}
+
+func TestRequestAflags(t *testing.T) {
+ aflags := newAflags(ssh_FILEXFER_ATTR_SIZE | ssh_FILEXFER_ATTR_UIDGID)
+ assert.True(t, aflags.Size)
+ assert.True(t, aflags.UidGid)
+ assert.False(t, aflags.Acmodtime)
+ assert.False(t, aflags.Permissions)
+}
+
+func TestRequestAttributes(t *testing.T) {
+ // UID/GID
+ fa := fileattrs{UID: 1, GID: 2}
+ fl := uint32(ssh_FILEXFER_ATTR_UIDGID)
+ at := []byte{}
+ at = marshalUint32(at, 1)
+ at = marshalUint32(at, 2)
+ test_fs, _ := getFileStat(fl, at)
+ assert.Equal(t, fa, fileattrs(*test_fs))
+ // Size and Mode
+ fa = fileattrs{Mode: 700, Size: 99}
+ fl = uint32(ssh_FILEXFER_ATTR_SIZE | ssh_FILEXFER_ATTR_PERMISSIONS)
+ at = []byte{}
+ at = marshalUint64(at, 99)
+ at = marshalUint32(at, 700)
+ test_fs, _ = getFileStat(fl, at)
+ test_fa := fileattrs(*test_fs)
+ assert.Equal(t, fa, test_fa)
+ // FileMode
+ assert.True(t, test_fa.FileMode().IsRegular())
+ assert.False(t, test_fa.FileMode().IsDir())
+ assert.Equal(t, test_fa.FileMode().Perm(), os.FileMode(700).Perm())
+}