]> git.r.bdr.sh - rbdr/mobius/commitdiff
Add tests and clean up
authorJeff Halter <redacted>
Wed, 11 Aug 2021 01:52:46 +0000 (18:52 -0700)
committerJeff Halter <redacted>
Tue, 10 Aug 2021 20:52:46 +0000 (13:52 -0700)
19 files changed:
hotline/client.go
hotline/client_conn.go
hotline/client_conn_test.go
hotline/file_header.go
hotline/file_name_with_info.go
hotline/file_name_with_info_test.go
hotline/file_path.go
hotline/file_path_test.go [new file with mode: 0644]
hotline/files.go
hotline/flattened_file_object.go
hotline/flattened_file_object_test.go
hotline/news.go
hotline/news_test.go [new file with mode: 0644]
hotline/server.go
hotline/transaction.go
hotline/transaction_handlers.go
hotline/transaction_handlers_test.go
hotline/transaction_test.go
hotline/ui.go

index 493d59e9473af71e53ab1bb8b49300d5141ffd14..dd113c9974438e7fc14ddfdd3d06c89497c8f11e 100644 (file)
@@ -90,7 +90,7 @@ type Client struct {
 
        UI *UI
 
 
        UI *UI
 
-       Inbox  chan *Transaction
+       Inbox chan *Transaction
 }
 
 func NewClient(cfgPath string, logger *zap.SugaredLogger) *Client {
 }
 
 func NewClient(cfgPath string, logger *zap.SugaredLogger) *Client {
@@ -264,10 +264,10 @@ func handleGetFileNameList(c *Client, t *Transaction) (res []Transaction, err er
 
                        entry := selectedNode.GetReference().(*FileNameWithInfo)
 
 
                        entry := selectedNode.GetReference().(*FileNameWithInfo)
 
-                       if bytes.Equal(entry.Type, []byte("fldr")) {
-                               c.Logger.Infow("get new directory listing", "name", string(entry.Name))
+                       if bytes.Equal(entry.Type[:], []byte("fldr")) {
+                               c.Logger.Infow("get new directory listing", "name", string(entry.name))
 
 
-                               c.filePath = append(c.filePath, string(entry.Name))
+                               c.filePath = append(c.filePath, string(entry.name))
                                f := NewField(fieldFilePath, EncodeFilePath(strings.Join(c.filePath, "/")))
 
                                if err := c.UI.HLClient.Send(*NewTransaction(tranGetFileNameList, nil, f)); err != nil {
                                f := NewField(fieldFilePath, EncodeFilePath(strings.Join(c.filePath, "/")))
 
                                if err := c.UI.HLClient.Send(*NewTransaction(tranGetFileNameList, nil, f)); err != nil {
@@ -275,7 +275,7 @@ func handleGetFileNameList(c *Client, t *Transaction) (res []Transaction, err er
                                }
                        } else {
                                // TODO: initiate file download
                                }
                        } else {
                                // TODO: initiate file download
-                               c.Logger.Infow("download file", "name", string(entry.Name))
+                               c.Logger.Infow("download file", "name", string(entry.name))
                        }
                }
 
                        }
                }
 
@@ -289,16 +289,19 @@ func handleGetFileNameList(c *Client, t *Transaction) (res []Transaction, err er
 
        for _, f := range t.Fields {
                var fn FileNameWithInfo
 
        for _, f := range t.Fields {
                var fn FileNameWithInfo
-               _, _ = fn.Read(f.Data)
+               err = fn.UnmarshalBinary(f.Data)
+               if err != nil {
+                       return nil, nil
+               }
 
 
-               if bytes.Equal(fn.Type, []byte("fldr")) {
-                       node := tview.NewTreeNode(fmt.Sprintf("[blue::]📁 %s[-:-:-]", fn.Name))
+               if bytes.Equal(fn.Type[:], []byte("fldr")) {
+                       node := tview.NewTreeNode(fmt.Sprintf("[blue::]📁 %s[-:-:-]", fn.name))
                        node.SetReference(&fn)
                        root.AddChild(node)
                } else {
                        node.SetReference(&fn)
                        root.AddChild(node)
                } else {
-                       size := binary.BigEndian.Uint32(fn.FileSize) / 1024
+                       size := binary.BigEndian.Uint32(fn.FileSize[:]) / 1024
 
 
-                       node := tview.NewTreeNode(fmt.Sprintf("   %-40s %10v KB", fn.Name, size))
+                       node := tview.NewTreeNode(fmt.Sprintf("   %-40s %10v KB", fn.name, size))
                        node.SetReference(&fn)
                        root.AddChild(node)
                }
                        node.SetReference(&fn)
                        root.AddChild(node)
                }
@@ -494,7 +497,7 @@ func handleClientTranShowAgreement(c *Client, t *Transaction) (res []Transaction
        agreement := string(t.GetField(fieldData).Data)
        agreement = strings.ReplaceAll(agreement, "\r", "\n")
 
        agreement := string(t.GetField(fieldData).Data)
        agreement = strings.ReplaceAll(agreement, "\r", "\n")
 
-       c.UI.agreeModal = tview.NewModal().
+       agreeModal := tview.NewModal().
                SetText(agreement).
                AddButtons([]string{"Agree", "Disagree"}).
                SetDoneFunc(func(buttonIndex int, buttonLabel string) {
                SetText(agreement).
                AddButtons([]string{"Agree", "Disagree"}).
                SetDoneFunc(func(buttonIndex int, buttonLabel string) {
@@ -517,10 +520,7 @@ func handleClientTranShowAgreement(c *Client, t *Transaction) (res []Transaction
                },
                )
 
                },
                )
 
-       c.Logger.Debug("show agreement page")
-       c.UI.Pages.AddPage("agreement", c.UI.agreeModal, false, true)
-       c.UI.Pages.ShowPage("agreement ")
-       c.UI.App.Draw()
+       c.UI.Pages.AddPage("agreement", agreeModal, false, true)
 
        return res, err
 }
 
        return res, err
 }
@@ -619,7 +619,7 @@ func (c *Client) Handshake() error {
                return err
        }
 
                return err
        }
 
-       if bytes.Compare(replyBuf, ServerHandshake) == 0 {
+       if bytes.Equal(replyBuf, ServerHandshake) {
                return nil
        }
 
                return nil
        }
 
@@ -653,7 +653,11 @@ func (c *Client) Send(t Transaction) error {
 
        var n int
        var err error
 
        var n int
        var err error
-       if n, err = c.Connection.Write(t.Payload()); err != nil {
+       b, err := t.MarshalBinary()
+       if err != nil {
+               return err
+       }
+       if n, err = c.Connection.Write(b); err != nil {
                return err
        }
        c.Logger.Debugw("Sent Transaction",
                return err
        }
        c.Logger.Debugw("Sent Transaction",
index eea3790346d659d74b21e57e63197414020ef2b2..367b70fdd9f1d92cab447d7035648aaf3e2756f1 100644 (file)
@@ -29,7 +29,7 @@ type ClientConn struct {
        ID         *[]byte
        Icon       *[]byte
        Flags      *[]byte
        ID         *[]byte
        Icon       *[]byte
        Flags      *[]byte
-       UserName   *[]byte
+       UserName   []byte
        Account    *Account
        IdleTime   *int
        Server     *Server
        Account    *Account
        IdleTime   *int
        Server     *Server
@@ -55,7 +55,7 @@ func (cc *ClientConn) handleTransaction(transaction *Transaction) error {
                        if field.ID == nil {
                                cc.Server.Logger.Infow(
                                        "Missing required field",
                        if field.ID == nil {
                                cc.Server.Logger.Infow(
                                        "Missing required field",
-                                       "Account", cc.Account.Login, "UserName", string(*cc.UserName), "RequestType", handler.Name, "FieldID", reqField.ID,
+                                       "Account", cc.Account.Login, "UserName", string(cc.UserName), "RequestType", handler.Name, "FieldID", reqField.ID,
                                )
                                return nil
                        }
                                )
                                return nil
                        }
@@ -63,7 +63,7 @@ func (cc *ClientConn) handleTransaction(transaction *Transaction) error {
                        if len(field.Data) < reqField.minLen {
                                cc.Server.Logger.Infow(
                                        "Field does not meet minLen",
                        if len(field.Data) < reqField.minLen {
                                cc.Server.Logger.Infow(
                                        "Field does not meet minLen",
-                                       "Account", cc.Account.Login, "UserName", string(*cc.UserName), "RequestType", handler.Name, "FieldID", reqField.ID,
+                                       "Account", cc.Account.Login, "UserName", string(cc.UserName), "RequestType", handler.Name, "FieldID", reqField.ID,
                                )
                                return nil
                        }
                                )
                                return nil
                        }
@@ -71,7 +71,7 @@ func (cc *ClientConn) handleTransaction(transaction *Transaction) error {
                if !authorize(cc.Account.Access, handler.Access) {
                        cc.Server.Logger.Infow(
                                "Unauthorized Action",
                if !authorize(cc.Account.Access, handler.Access) {
                        cc.Server.Logger.Infow(
                                "Unauthorized Action",
-                               "Account", cc.Account.Login, "UserName", string(*cc.UserName), "RequestType", handler.Name,
+                               "Account", cc.Account.Login, "UserName", string(cc.UserName), "RequestType", handler.Name,
                        )
                        cc.Server.outbox <- cc.NewErrReply(transaction, handler.DenyMsg)
 
                        )
                        cc.Server.outbox <- cc.NewErrReply(transaction, handler.DenyMsg)
 
@@ -81,7 +81,7 @@ func (cc *ClientConn) handleTransaction(transaction *Transaction) error {
                cc.Server.Logger.Infow(
                        "Received Transaction",
                        "login", cc.Account.Login,
                cc.Server.Logger.Infow(
                        "Received Transaction",
                        "login", cc.Account.Login,
-                       "name", string(*cc.UserName),
+                       "name", string(cc.UserName),
                        "RequestType", handler.Name,
                )
 
                        "RequestType", handler.Name,
                )
 
@@ -95,7 +95,7 @@ func (cc *ClientConn) handleTransaction(transaction *Transaction) error {
        } else {
                cc.Server.Logger.Errorw(
                        "Unimplemented transaction type received",
        } else {
                cc.Server.Logger.Errorw(
                        "Unimplemented transaction type received",
-                       "UserName", string(*cc.UserName), "RequestID", requestNum,
+                       "UserName", string(cc.UserName), "RequestID", requestNum,
                )
        }
 
                )
        }
 
@@ -114,7 +114,7 @@ func (cc *ClientConn) handleTransaction(transaction *Transaction) error {
                        tranNotifyChangeUser,
                        NewField(fieldUserID, *cc.ID),
                        NewField(fieldUserFlags, *cc.Flags),
                        tranNotifyChangeUser,
                        NewField(fieldUserID, *cc.ID),
                        NewField(fieldUserFlags, *cc.Flags),
-                       NewField(fieldUserName, *cc.UserName),
+                       NewField(fieldUserName, cc.UserName),
                        NewField(fieldUserIconID, *cc.Icon),
                )
 
                        NewField(fieldUserIconID, *cc.Icon),
                )
 
index 80c22b471edb7abc993d80a203aaf87bfcefc5b8..6e3464f30f0bca55803a74b8d60cbedbb4bc61c0 100644 (file)
@@ -11,7 +11,7 @@ func TestClientConn_handleTransaction(t *testing.T) {
                ID         *[]byte
                Icon       *[]byte
                Flags      *[]byte
                ID         *[]byte
                Icon       *[]byte
                Flags      *[]byte
-               UserName   *[]byte
+               UserName   []byte
                Account    *Account
                IdleTime   *int
                Server     *Server
                Account    *Account
                IdleTime   *int
                Server     *Server
index 60c652e36eb5cf1be776b192ed5bdc822cc71315..7e0df1666c54757f7bcd8841400ebb35f5bdaf20 100644 (file)
@@ -27,6 +27,15 @@ func NewFileHeader(fileName string, isDir bool) FileHeader {
        return fh
 }
 
        return fh
 }
 
+func (fh *FileHeader) Read(p []byte) (n int, err error) {
+       p = concat.Slices(
+               fh.Size,
+               fh.Type,
+               fh.FilePath,
+       )
+       return len(p), nil
+}
+
 func (fh *FileHeader) Payload() []byte {
        return concat.Slices(
                fh.Size,
 func (fh *FileHeader) Payload() []byte {
        return concat.Slices(
                fh.Size,
index eecf5ec0552e45523324a832924d12dd99bd95ad..d9add8cc6a4f16142c3c6063279a91af68a491f0 100644 (file)
@@ -1,53 +1,52 @@
 package hotline
 
 import (
 package hotline
 
 import (
+       "bytes"
        "encoding/binary"
        "encoding/binary"
-       "github.com/jhalter/mobius/concat"
 )
 
 )
 
-// FileNameWithInfo field content is presented in this structure:
-// Type        4               Folder (‘fldr’) or other
-// Creator     4
-// File size   4
-// 4           Reserved?
-// Name script 2
-// Name size   2
-// Name data   size
 type FileNameWithInfo struct {
 type FileNameWithInfo struct {
-       Type       []byte // file type code
-       Creator    []byte // File creator code
-       FileSize   []byte // File Size in bytes
-       RSVD       []byte
-       NameScript []byte // TODO: What is this?
-       NameSize   []byte // Length of name field
-       Name       []byte // File name
+       fileNameWithInfoHeader
+       name []byte // File name
 }
 
 }
 
-func (f FileNameWithInfo) Payload() []byte {
-       name := f.Name
-       nameSize := make([]byte, 2)
-       binary.BigEndian.PutUint16(nameSize, uint16(len(name)))
-
-       return concat.Slices(
-               f.Type,
-               f.Creator,
-               f.FileSize,
-               []byte{0, 0, 0, 0},
-               f.NameScript,
-               nameSize,
-               f.Name,
-       )
+// fileNameWithInfoHeader contains the fixed length fields of FileNameWithInfo
+type fileNameWithInfoHeader struct {
+       Type       [4]byte // file type code
+       Creator    [4]byte // File creator code
+       FileSize   [4]byte // File Size in bytes
+       RSVD       [4]byte
+       NameScript [2]byte // ??
+       NameSize   [2]byte // Length of name field
 }
 
 }
 
-func (f *FileNameWithInfo) Read(p []byte) (n int, err error) {
-       // TODO: check p for expected len
-       f.Type = p[0:4]
-       f.Creator = p[4:8]
-       f.FileSize = p[8:12]
-       f.RSVD = p[12:16]
-       f.NameScript = p[16:18]
-       f.NameSize = p[18:20]
-       f.Name = p[20:]
-
-       return len(p), err
+func (f *fileNameWithInfoHeader) nameLen() int {
+       return int(binary.BigEndian.Uint16(f.NameSize[:]))
 }
 }
+
+func (f *FileNameWithInfo) MarshalBinary() (data []byte, err error) {
+       var buf bytes.Buffer
+       err = binary.Write(&buf, binary.LittleEndian, f.fileNameWithInfoHeader)
+       if err != nil {
+               return data, err
+       }
+
+       _, err = buf.Write(f.name)
+       if err != nil {
+               return data, err
+       }
+
+       return buf.Bytes(), err
+}
+
+func (f *FileNameWithInfo) UnmarshalBinary(data []byte) error {
+       err := binary.Read(bytes.NewReader(data), binary.BigEndian, &f.fileNameWithInfoHeader)
+       if err != nil {
+               return err
+       }
+       headerLen := binary.Size(f.fileNameWithInfoHeader)
+       f.name = data[headerLen : headerLen+f.nameLen()]
+
+       return err
+}
+
index 15ec0d379fd7613fbc5675c01ec4d055e47fae65..3ca2c3089131ba32fce2b8afca42b4d105a3b5d1 100644 (file)
@@ -2,34 +2,83 @@ package hotline
 
 import (
        "github.com/stretchr/testify/assert"
 
 import (
        "github.com/stretchr/testify/assert"
+       "reflect"
        "testing"
 )
 
        "testing"
 )
 
-func TestFileNameWithInfo_Read(t *testing.T) {
+func TestFileNameWithInfo_MarshalBinary(t *testing.T) {
        type fields struct {
        type fields struct {
-               Type       []byte
-               Creator    []byte
-               FileSize   []byte
-               NameScript []byte
-               NameSize   []byte
-               Name       []byte
+               fileNameWithInfoHeader fileNameWithInfoHeader
+               name                   []byte
+       }
+       tests := []struct {
+               name     string
+               fields   fields
+               wantData []byte
+               wantErr  bool
+       }{
+               {
+                       name: "returns expected bytes",
+                       fields: fields{
+                               fileNameWithInfoHeader: fileNameWithInfoHeader{
+                                       Type:       [4]byte{0x54, 0x45, 0x58, 0x54}, // TEXT
+                                       Creator:    [4]byte{0x54, 0x54, 0x58, 0x54}, // TTXT
+                                       FileSize:   [4]byte{0x00, 0x43, 0x16, 0xd3}, // File Size
+                                       RSVD:       [4]byte{0, 0, 0, 0},
+                                       NameScript: [2]byte{0, 0},
+                                       NameSize:   [2]byte{0x00, 0x03},
+                               },
+                               name: []byte("foo"),
+                       },
+                       wantData: []byte{
+                               0x54, 0x45, 0x58, 0x54,
+                               0x54, 0x54, 0x58, 0x54,
+                               0x00, 0x43, 0x16, 0xd3,
+                               0, 0, 0, 0,
+                               0, 0,
+                               0x00, 0x03,
+                               0x66, 0x6f, 0x6f,
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       f := &FileNameWithInfo{
+                               fileNameWithInfoHeader: tt.fields.fileNameWithInfoHeader,
+                               name:                   tt.fields.name,
+                       }
+                       gotData, err := f.MarshalBinary()
+                       if (err != nil) != tt.wantErr {
+                               t.Errorf("MarshalBinary() error = %v, wantErr %v", err, tt.wantErr)
+                               return
+                       }
+                       if !reflect.DeepEqual(gotData, tt.wantData) {
+                               t.Errorf("MarshalBinary() gotData = %v, want %v", gotData, tt.wantData)
+                       }
+               })
+       }
+}
+
+func TestFileNameWithInfo_UnmarshalBinary(t *testing.T) {
+       type fields struct {
+               fileNameWithInfoHeader fileNameWithInfoHeader
+               name                   []byte
        }
        type args struct {
        }
        type args struct {
-               p []byte
+               data []byte
        }
        tests := []struct {
                name    string
                fields  fields
                args    args
        }
        tests := []struct {
                name    string
                fields  fields
                args    args
-               want    *FileNameWithInfo
-               wantN   int
+               want *FileNameWithInfo
                wantErr bool
        }{
                {
                wantErr bool
        }{
                {
-                       name:   "reads bytes into struct",
-                       fields: fields{},
+                       name: "writes bytes into struct",
                        args: args{
                        args: args{
-                               p: []byte{
+                               data: []byte{
                                        0x54, 0x45, 0x58, 0x54, // TEXT
                                        0x54, 0x54, 0x58, 0x54, // TTXT
                                        0x00, 0x43, 0x16, 0xd3, // File Size
                                        0x54, 0x45, 0x58, 0x54, // TEXT
                                        0x54, 0x54, 0x58, 0x54, // TTXT
                                        0x00, 0x43, 0x16, 0xd3, // File Size
@@ -40,40 +89,31 @@ func TestFileNameWithInfo_Read(t *testing.T) {
                                },
                        },
                        want: &FileNameWithInfo{
                                },
                        },
                        want: &FileNameWithInfo{
-                               Type:       []byte("TEXT"),
-                               Creator:    []byte("TTXT"),
-                               FileSize:   []byte{0x00, 0x43, 0x16, 0xd3},
-                               RSVD:       []byte{0, 0, 0, 0},
-                               NameScript: []byte{0, 0},
-                               NameSize:   []byte{0x00, 0x0e},
-                               Name:       []byte("Audion.app.zip"),
+                               fileNameWithInfoHeader: fileNameWithInfoHeader{
+                                       Type:       [4]byte{0x54, 0x45, 0x58, 0x54}, // TEXT
+                                       Creator:    [4]byte{0x54, 0x54, 0x58, 0x54}, // TTXT
+                                       FileSize:   [4]byte{0x00, 0x43, 0x16, 0xd3}, // File Size
+                                       RSVD:       [4]byte{0, 0, 0, 0},
+                                       NameScript: [2]byte{0, 0},
+                                       NameSize:   [2]byte{0x00, 0x0e},
+                               },
+                               name: []byte("Audion.app.zip"),
                        },
                        },
-                       wantN:   34,
                        wantErr: false,
                },
        }
        for _, tt := range tests {
                t.Run(tt.name, func(t *testing.T) {
                        f := &FileNameWithInfo{
                        wantErr: false,
                },
        }
        for _, tt := range tests {
                t.Run(tt.name, func(t *testing.T) {
                        f := &FileNameWithInfo{
-                               Type:       tt.fields.Type,
-                               Creator:    tt.fields.Creator,
-                               FileSize:   tt.fields.FileSize,
-                               NameScript: tt.fields.NameScript,
-                               NameSize:   tt.fields.NameSize,
-                               Name:       tt.fields.Name,
+                               fileNameWithInfoHeader: tt.fields.fileNameWithInfoHeader,
+                               name:                   tt.fields.name,
                        }
                        }
-                       gotN, err := f.Read(tt.args.p)
-                       if (err != nil) != tt.wantErr {
-                               t.Errorf("Read() error = %v, wantErr %v", err, tt.wantErr)
-                               return
-                       }
-                       if gotN != tt.wantN {
-                               t.Errorf("Read() gotN = %v, want %v", gotN, tt.wantN)
+                       if err := f.UnmarshalBinary(tt.args.data); (err != nil) != tt.wantErr {
+                               t.Errorf("UnmarshalBinary() error = %v, wantErr %v", err, tt.wantErr)
                        }
                        if !assert.Equal(t, tt.want, f) {
                                t.Errorf("Read() got = %v, want %v", f, tt.want)
                        }
                        if !assert.Equal(t, tt.want, f) {
                                t.Errorf("Read() got = %v, want %v", f, tt.want)
-
                        }
                })
        }
                        }
                })
        }
-}
+}
\ No newline at end of file
index 71168dcbcbcea5593a555ee12ffa3593b414b4b3..ae96e528633d02e464c0aa035aa1a27746c83981 100644 (file)
@@ -24,32 +24,30 @@ func NewFilePathItem(b []byte) FilePathItem {
 }
 
 type FilePath struct {
 }
 
 type FilePath struct {
-       PathItemCount []byte
-       PathItems     []FilePathItem
+       ItemCount []byte
+       Items     []FilePathItem
 }
 
 }
 
-func NewFilePath(b []byte) FilePath {
-       if b == nil {
-               return FilePath{}
-       }
-
-       fp := FilePath{PathItemCount: b[0:2]}
+func (fp *FilePath) UnmarshalBinary(b []byte) error {
+       fp.ItemCount = b[0:2]
 
 
-       // number of items in the path
-       pathItemLen := binary.BigEndian.Uint16(b[0:2])
        pathData := b[2:]
        pathData := b[2:]
-       for i := uint16(0); i < pathItemLen; i++ {
+       for i := uint16(0); i < fp.Len(); i++ {
                segLen := pathData[2]
                segLen := pathData[2]
-               fp.PathItems = append(fp.PathItems, NewFilePathItem(pathData[:segLen+3]))
+               fp.Items = append(fp.Items, NewFilePathItem(pathData[:segLen+3]))
                pathData = pathData[3+segLen:]
        }
 
                pathData = pathData[3+segLen:]
        }
 
-       return fp
+       return nil
+}
+
+func (fp *FilePath) Len() uint16 {
+       return binary.BigEndian.Uint16(fp.ItemCount)
 }
 
 func (fp *FilePath) String() string {
        var out []string
 }
 
 func (fp *FilePath) String() string {
        var out []string
-       for _, i := range fp.PathItems {
+       for _, i := range fp.Items {
                out = append(out, string(i.Name))
        }
        return strings.Join(out, pathSeparator)
                out = append(out, string(i.Name))
        }
        return strings.Join(out, pathSeparator)
diff --git a/hotline/file_path_test.go b/hotline/file_path_test.go
new file mode 100644 (file)
index 0000000..a5f5b60
--- /dev/null
@@ -0,0 +1,60 @@
+package hotline
+
+import (
+       "github.com/stretchr/testify/assert"
+       "testing"
+)
+
+func TestFilePath_UnmarshalBinary(t *testing.T) {
+       type fields struct {
+               ItemCount []byte
+               Items     []FilePathItem
+       }
+       type args struct {
+               b []byte
+       }
+       tests := []struct {
+               name    string
+               args    args
+               want    FilePath
+               wantErr bool
+       }{
+               {
+                       name: "unmarshals bytes into struct",
+                       args: args{b: []byte{
+                               0x00, 0x02,
+                               0x00, 0x00,
+                               0x0f,
+                               0x46, 0x69, 0x72, 0x73, 0x74, 0x20, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x20, 0x44, 0x69, 0x72,
+                               0x00, 0x00,
+                               0x08,
+                               0x41, 0x20, 0x53, 0x75, 0x62, 0x44, 0x69, 0x72,
+                       }},
+                       want: FilePath{
+                               ItemCount: []byte{0x00, 0x02},
+                               Items: []FilePathItem{
+                                       {
+                                               Len:  0x0f,
+                                               Name: []byte("First Level Dir"),
+                                       },
+                                       {
+                                               Len:  0x08,
+                                               Name: []byte("A SubDir"),
+                                       },
+                               },
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       var fp FilePath
+                       if err := fp.UnmarshalBinary(tt.args.b); (err != nil) != tt.wantErr {
+                               t.Errorf("UnmarshalBinary() error = %v, wantErr %v", err, tt.wantErr)
+                       }
+                       if !assert.Equal(t, tt.want, fp) {
+                               t.Errorf("Read() got = %v, want %v", fp, tt.want)
+                       }
+               })
+       }
+}
index 1db698baa45a9e74b76e50495e2a651bb86148a5..bccab35868e0277ce50fad2c0ab25f2898951349 100644 (file)
@@ -53,13 +53,16 @@ func getFileNameList(filePath string) ([]Field, error) {
 
        for _, file := range files {
                var fileType []byte
 
        for _, file := range files {
                var fileType []byte
+               var fnwi FileNameWithInfo
                fileCreator := make([]byte, 4)
                fileCreator := make([]byte, 4)
-               fileSize := make([]byte, 4)
+               //fileSize := make([]byte, 4)
                if !file.IsDir() {
                        fileType = []byte(fileTypeFromFilename(file.Name()))
                        fileCreator = []byte(fileCreatorFromFilename(file.Name()))
 
                if !file.IsDir() {
                        fileType = []byte(fileTypeFromFilename(file.Name()))
                        fileCreator = []byte(fileCreatorFromFilename(file.Name()))
 
-                       binary.BigEndian.PutUint32(fileSize, uint32(file.Size()))
+                       binary.BigEndian.PutUint32(fnwi.FileSize[:], uint32(file.Size()))
+                       copy(fnwi.Type[:], fileType[:])
+                       copy(fnwi.Creator[:], fileCreator[:])
                } else {
                        fileType = []byte("fldr")
 
                } else {
                        fileType = []byte("fldr")
 
@@ -67,19 +70,22 @@ func getFileNameList(filePath string) ([]Field, error) {
                        if err != nil {
                                return fields, err
                        }
                        if err != nil {
                                return fields, err
                        }
-                       binary.BigEndian.PutUint32(fileSize, uint32(len(dir)))
+                       binary.BigEndian.PutUint32(fnwi.FileSize[:], uint32(len(dir)))
+                       copy(fnwi.Type[:], fileType[:])
+                       copy(fnwi.Creator[:], fileCreator[:])
                }
 
                }
 
-               fields = append(fields, NewField(
-                       fieldFileNameWithInfo,
-                       FileNameWithInfo{
-                               Type:       fileType,
-                               Creator:    fileCreator,
-                               FileSize:   fileSize,
-                               NameScript: []byte{0, 0},
-                               Name:       []byte(file.Name()),
-                       }.Payload(),
-               ))
+               nameSize := make([]byte, 2)
+               binary.BigEndian.PutUint16(nameSize, uint16(len(file.Name())))
+               copy(fnwi.NameSize[:], nameSize[:])
+
+               fnwi.name = []byte(file.Name())
+
+               b, err := fnwi.MarshalBinary()
+               if err != nil {
+                       return nil, err
+               }
+               fields = append(fields, NewField(fieldFileNameWithInfo, b))
        }
 
        return fields, nil
        }
 
        return fields, nil
@@ -150,6 +156,10 @@ func EncodeFilePath(filePath string) []byte {
 }
 
 func ReadFilePath(filePathFieldData []byte) string {
 }
 
 func ReadFilePath(filePathFieldData []byte) string {
-       fp := NewFilePath(filePathFieldData)
+       var fp FilePath
+       err := fp.UnmarshalBinary(filePathFieldData)
+       if err != nil {
+               // TODO
+       }
        return fp.String()
 }
        return fp.String()
 }
index dfdd8a871770ce3136f75828a02e8e1efb4f57b1..8e2dbff750569e5640a83ad3f495db083abda5e1 100644 (file)
@@ -16,19 +16,19 @@ type flattenedFileObject struct {
 
 // FlatFileHeader is the first section of a "Flattened File Object".  All fields have static values.
 type FlatFileHeader struct {
 
 // FlatFileHeader is the first section of a "Flattened File Object".  All fields have static values.
 type FlatFileHeader struct {
-       Format    []byte // Always "FILP"
-       Version   []byte // Always 1
-       RSVD      []byte // Always empty zeros
-       ForkCount []byte // Always 2
+       Format    [4]byte  // Always "FILP"
+       Version   [2]byte  // Always 1
+       RSVD      [16]byte // Always empty zeros
+       ForkCount [2]byte  // Always 2
 }
 
 // NewFlatFileHeader returns a FlatFileHeader struct
 func NewFlatFileHeader() FlatFileHeader {
        return FlatFileHeader{
 }
 
 // NewFlatFileHeader returns a FlatFileHeader struct
 func NewFlatFileHeader() FlatFileHeader {
        return FlatFileHeader{
-               Format:    []byte("FILP"),
-               Version:   []byte{0, 1},
-               RSVD:      make([]byte, 16),
-               ForkCount: []byte{0, 2},
+               Format:    [4]byte{0x46, 0x49, 0x4c, 0x50}, // FILP
+               Version:   [2]byte{0, 1},
+               RSVD:      [16]byte{},
+               ForkCount: [2]byte{0, 2},
        }
 }
 
        }
 }
 
@@ -138,12 +138,7 @@ func ReadFlattenedFileObject(bytes []byte) flattenedFileObject {
        //dataSize := binary.BigEndian.Uint32(dataSizeField)
 
        ffo := flattenedFileObject{
        //dataSize := binary.BigEndian.Uint32(dataSizeField)
 
        ffo := flattenedFileObject{
-               FlatFileHeader: FlatFileHeader{
-                       Format:    bytes[0:4],
-                       Version:   bytes[4:6],
-                       RSVD:      bytes[6:22],
-                       ForkCount: bytes[22:24],
-               },
+               FlatFileHeader: NewFlatFileHeader(),
                FlatFileInformationForkHeader: FlatFileInformationForkHeader{
                        ForkType:        bytes[24:28],
                        CompressionType: bytes[28:32],
                FlatFileInformationForkHeader: FlatFileInformationForkHeader{
                        ForkType:        bytes[24:28],
                        CompressionType: bytes[28:32],
@@ -178,10 +173,10 @@ func ReadFlattenedFileObject(bytes []byte) flattenedFileObject {
 
 func (f flattenedFileObject) Payload() []byte {
        var out []byte
 
 func (f flattenedFileObject) Payload() []byte {
        var out []byte
-       out = append(out, f.FlatFileHeader.Format...)
-       out = append(out, f.FlatFileHeader.Version...)
-       out = append(out, f.FlatFileHeader.RSVD...)
-       out = append(out, f.FlatFileHeader.ForkCount...)
+       out = append(out, f.FlatFileHeader.Format[:]...)
+       out = append(out, f.FlatFileHeader.Version[:]...)
+       out = append(out, f.FlatFileHeader.RSVD[:]...)
+       out = append(out, f.FlatFileHeader.ForkCount[:]...)
 
        out = append(out, []byte("INFO")...)
        out = append(out, []byte{0, 0, 0, 0}...)
 
        out = append(out, []byte("INFO")...)
        out = append(out, []byte{0, 0, 0, 0}...)
@@ -211,22 +206,22 @@ func (f flattenedFileObject) Payload() []byte {
        return out
 }
 
        return out
 }
 
-func NewFlattenedFileObject(filePath string, fileName string) (flattenedFileObject, error) {
+func NewFlattenedFileObject(filePath, fileName string) (*flattenedFileObject, error) {
        file, err := os.Open(fmt.Sprintf("%v/%v", filePath, fileName))
        if err != nil {
        file, err := os.Open(fmt.Sprintf("%v/%v", filePath, fileName))
        if err != nil {
-               return flattenedFileObject{}, err
+               return nil, err
        }
        defer file.Close()
 
        fileInfo, err := file.Stat()
        if err != nil {
        }
        defer file.Close()
 
        fileInfo, err := file.Stat()
        if err != nil {
-               return flattenedFileObject{}, err
+               return nil, err
        }
 
        dataSize := make([]byte, 4)
        binary.BigEndian.PutUint32(dataSize, uint32(fileInfo.Size()))
 
        }
 
        dataSize := make([]byte, 4)
        binary.BigEndian.PutUint32(dataSize, uint32(fileInfo.Size()))
 
-       return flattenedFileObject{
+       return &flattenedFileObject{
                FlatFileHeader:          NewFlatFileHeader(),
                FlatFileInformationFork: NewFlatFileInformationFork(fileName),
                FlatFileDataForkHeader: FlatFileDataForkHeader{
                FlatFileHeader:          NewFlatFileHeader(),
                FlatFileInformationFork: NewFlatFileInformationFork(fileName),
                FlatFileDataForkHeader: FlatFileDataForkHeader{
index 63e71757ea01c427e806e8eae3e10b7720c61b14..19b7c94afbbb22da78f7b7d5918a0e511cbea5cc 100644 (file)
@@ -3,6 +3,8 @@ package hotline
 import (
        "bytes"
        "encoding/hex"
 import (
        "bytes"
        "encoding/hex"
+       "github.com/davecgh/go-spew/spew"
+       "reflect"
        "testing"
 )
 
        "testing"
 )
 
@@ -11,7 +13,7 @@ func TestReadFlattenedFileObject(t *testing.T) {
 
        ffo := ReadFlattenedFileObject(testData)
 
 
        ffo := ReadFlattenedFileObject(testData)
 
-       format := ffo.FlatFileHeader.Format
+       format := ffo.FlatFileHeader.Format[:]
        want := []byte("FILP")
        if !bytes.Equal(format, want) {
                t.Errorf("ReadFlattenedFileObject() = %q, want %q", format, want)
        want := []byte("FILP")
        if !bytes.Equal(format, want) {
                t.Errorf("ReadFlattenedFileObject() = %q, want %q", format, want)
@@ -34,3 +36,59 @@ func TestReadFlattenedFileObject(t *testing.T) {
 //             t.Errorf("%q, want %q", comment, want)
 //     }
 //}
 //             t.Errorf("%q, want %q", comment, want)
 //     }
 //}
+
+func TestNewFlattenedFileObject(t *testing.T) {
+       type args struct {
+               filePath string
+               fileName string
+       }
+       tests := []struct {
+               name    string
+               args    args
+               want    *flattenedFileObject
+               wantErr bool
+       }{
+               {
+                       name: "when file path is valid",
+                       args: args{
+                               filePath: "./test/config/Files/",
+                               fileName: "testfile.txt",
+                       },
+                       want: &flattenedFileObject{
+                               FlatFileHeader:                NewFlatFileHeader(),
+                               FlatFileInformationForkHeader: FlatFileInformationForkHeader{},
+                               FlatFileInformationFork:       NewFlatFileInformationFork("testfile.txt"),
+                               FlatFileDataForkHeader:        FlatFileDataForkHeader{
+                                       ForkType:        []byte("DATA"),
+                                       CompressionType: []byte{0, 0, 0, 0},
+                                       RSVD:            []byte{0, 0, 0, 0},
+                                       DataSize:        []byte{0x00, 0x00, 0x00, 0x17},
+                               },
+                               FileData:                      nil,
+                       },
+                       wantErr: false,
+               },
+               {
+                       name: "when file path is invalid",
+                       args: args{
+                               filePath: "./nope/",
+                               fileName: "also-nope.txt",
+                       },
+                       want: nil,
+                       wantErr: true,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       got, err := NewFlattenedFileObject(tt.args.filePath, tt.args.fileName)
+                       spew.Dump(got)
+                       if (err != nil) != tt.wantErr {
+                               t.Errorf("NewFlattenedFileObject() error = %v, wantErr %v", err, tt.wantErr)
+                               return
+                       }
+                       if !reflect.DeepEqual(got, tt.want) {
+                               t.Errorf("NewFlattenedFileObject() got = %v, want %v", got, tt.want)
+                       }
+               })
+       }
+}
\ No newline at end of file
index a9e5d723277281c819c4c8830fe53dd763184674..7a5c30c784338fb82680d4ca2002f7542067f7c2 100644 (file)
@@ -4,7 +4,6 @@ import (
        "bytes"
        "crypto/rand"
        "encoding/binary"
        "bytes"
        "crypto/rand"
        "encoding/binary"
-       "log"
        "sort"
        "time"
 )
        "sort"
        "time"
 )
@@ -14,14 +13,15 @@ type ThreadedNews struct {
 }
 
 type NewsCategoryListData15 struct {
 }
 
 type NewsCategoryListData15 struct {
-       Type     []byte                            `yaml:"Type"`     //Size 2 ; Bundle (2) or category (3)
+       Type     []byte `yaml:"Type"` //Size 2 ; Bundle (2) or category (3)
+       Count    []byte // Article or SubCategory count Size 2
+       NameSize byte
        Name     string                            `yaml:"Name"`     //
        Articles map[uint32]*NewsArtData           `yaml:"Articles"` // Optional, if Type is Category
        SubCats  map[string]NewsCategoryListData15 `yaml:"SubCats"`
        Name     string                            `yaml:"Name"`     //
        Articles map[uint32]*NewsArtData           `yaml:"Articles"` // Optional, if Type is Category
        SubCats  map[string]NewsCategoryListData15 `yaml:"SubCats"`
-       Count    []byte                            // Article or SubCategory count Size 2
+       GUID     []byte                            // Size 16
        AddSN    []byte                            // Size 4
        DeleteSN []byte                            // Size 4
        AddSN    []byte                            // Size 4
        DeleteSN []byte                            // Size 4
-       GUID     []byte                            // Size 16
 }
 
 func (newscat *NewsCategoryListData15) GetNewsArtListData() NewsArtListData {
 }
 
 func (newscat *NewsCategoryListData15) GetNewsArtListData() NewsArtListData {
@@ -61,7 +61,7 @@ func (newscat *NewsCategoryListData15) GetNewsArtListData() NewsArtListData {
        return nald
 }
 
        return nald
 }
 
-// NewsArtData repsents a single news article
+// NewsArtData represents single news article
 type NewsArtData struct {
        Title         string `yaml:"Title"`
        Poster        string `yaml:"Poster"`
 type NewsArtData struct {
        Title         string `yaml:"Title"`
        Poster        string `yaml:"Poster"`
@@ -102,7 +102,7 @@ func (nald *NewsArtListData) Payload() []byte {
        return out
 }
 
        return out
 }
 
-// NewsArtList is a summarized ver sion of a NewArtData record for display in list view
+// NewsArtList is a summarized version of a NewArtData record for display in list view
 type NewsArtList struct {
        ID          []byte // Size 4
        TimeStamp   []byte // Year (2 bytes), milliseconds (2 bytes) and seconds (4 bytes)
 type NewsArtList struct {
        ID          []byte // Size 4
        TimeStamp   []byte // Year (2 bytes), milliseconds (2 bytes) and seconds (4 bytes)
@@ -154,18 +154,18 @@ type NewsFlavorList struct {
        // Article size 2
 }
 
        // Article size 2
 }
 
-func (newscat *NewsCategoryListData15) Payload() []byte {
+func (newscat *NewsCategoryListData15) MarshalBinary() (data []byte, err error) {
        count := make([]byte, 2)
        binary.BigEndian.PutUint16(count, uint16(len(newscat.Articles)+len(newscat.SubCats)))
 
        out := append(newscat.Type, count...)
 
        if bytes.Equal(newscat.Type, []byte{0, 3}) {
        count := make([]byte, 2)
        binary.BigEndian.PutUint16(count, uint16(len(newscat.Articles)+len(newscat.SubCats)))
 
        out := append(newscat.Type, count...)
 
        if bytes.Equal(newscat.Type, []byte{0, 3}) {
-               // Generate a random GUID
+               // Generate a random GUID // TODO: does this need to be random?
                b := make([]byte, 16)
                _, err := rand.Read(b)
                if err != nil {
                b := make([]byte, 16)
                _, err := rand.Read(b)
                if err != nil {
-                       log.Fatal(err)
+                       return data, err
                }
 
                out = append(out, b...)                  // GUID
                }
 
                out = append(out, b...)                  // GUID
@@ -176,7 +176,7 @@ func (newscat *NewsCategoryListData15) Payload() []byte {
        out = append(out, newscat.nameLen()...)
        out = append(out, []byte(newscat.Name)...)
 
        out = append(out, newscat.nameLen()...)
        out = append(out, []byte(newscat.Name)...)
 
-       return out
+       return out, err
 }
 
 // ReadNewsCategoryListData parses a byte slice into a NewsCategoryListData15 struct
 }
 
 // ReadNewsCategoryListData parses a byte slice into a NewsCategoryListData15 struct
@@ -203,26 +203,26 @@ func (newscat *NewsCategoryListData15) nameLen() []byte {
        return []byte{uint8(len(newscat.Name))}
 }
 
        return []byte{uint8(len(newscat.Name))}
 }
 
-type NewsPath struct {
-       Paths []string
-}
-
-func (np *NewsPath) Payload() []byte {
-       var out []byte
-
-       count := make([]byte, 2)
-       binary.BigEndian.PutUint16(count, uint16(len(np.Paths)))
-
-       out = append(out, count...)
-       for _, p := range np.Paths {
-               pLen := byte(len(p))
-               out = append(out, []byte{0, 0}...) // what is this?
-               out = append(out, pLen)
-               out = append(out, []byte(p)...)
-       }
-
-       return out
-}
+//type NewsPath struct {
+//     Paths []string
+//}
+//
+//func (np *NewsPath) Payload() []byte {
+//     var out []byte
+//
+//     count := make([]byte, 2)
+//     binary.BigEndian.PutUint16(count, uint16(len(np.Paths)))
+//
+//     out = append(out, count...)
+//     for _, p := range np.Paths {
+//             pLen := byte(len(p))
+//             out = append(out, []byte{0, 0}...) // what is this?
+//             out = append(out, pLen)
+//             out = append(out, []byte(p)...)
+//     }
+//
+//     return out
+//}
 
 func ReadNewsPath(newsPath []byte) []string {
        if len(newsPath) == 0 {
 
 func ReadNewsPath(newsPath []byte) []string {
        if len(newsPath) == 0 {
diff --git a/hotline/news_test.go b/hotline/news_test.go
new file mode 100644 (file)
index 0000000..02e11a9
--- /dev/null
@@ -0,0 +1,100 @@
+package hotline
+
+import (
+       "bytes"
+       "reflect"
+       "testing"
+)
+
+func TestNewsCategoryListData15_MarshalBinary(t *testing.T) {
+       type fields struct {
+               Type     []byte
+               Name     string
+               Articles map[uint32]*NewsArtData
+               SubCats  map[string]NewsCategoryListData15
+               Count    []byte
+               AddSN    []byte
+               DeleteSN []byte
+               GUID     []byte
+       }
+       tests := []struct {
+               name     string
+               fields   fields
+               wantData []byte
+               wantErr  bool
+       }{
+               {
+                       name: "returns expected bytes when type is a bundle",
+                       fields: fields{
+                               Type: []byte{0x00, 0x02},
+                               Articles: map[uint32]*NewsArtData{
+                                       uint32(1): {
+                                               Title:  "",
+                                               Poster: "",
+                                               Data:   "",
+                                       },
+                               },
+                               Name: "foo",
+                       },
+                       wantData: []byte{
+                               0x00, 0x02,
+                               0x00, 0x01,
+                               0x03,
+                               0x66, 0x6f, 0x6f,
+                       },
+                       wantErr: false,
+               },
+               {
+                       name: "returns expected bytes when type is a category",
+                       fields: fields{
+                               Type: []byte{0x00, 0x03},
+                               Articles: map[uint32]*NewsArtData{
+                                       uint32(1): {
+                                               Title:  "",
+                                               Poster: "",
+                                               Data:   "",
+                                       },
+                               },
+                               Name: "foo",
+                       },
+                       wantData: []byte{
+                               0x00, 0x03,
+                               0x00, 0x01,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x01,
+                               0x00, 0x00, 0x00, 0x02,
+                               0x03,
+                               0x66, 0x6f, 0x6f,
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       newscat := &NewsCategoryListData15{
+                               Type:     tt.fields.Type,
+                               Name:     tt.fields.Name,
+                               Articles: tt.fields.Articles,
+                               SubCats:  tt.fields.SubCats,
+                               Count:    tt.fields.Count,
+                               AddSN:    tt.fields.AddSN,
+                               DeleteSN: tt.fields.DeleteSN,
+                               GUID:     tt.fields.GUID,
+                       }
+                       gotData, err := newscat.MarshalBinary()
+                       if bytes.Equal(newscat.Type, []byte{0, 3}) {
+                               // zero out the random GUID before comparison
+                               for i := 4; i < 20; i++ {
+                                       gotData[i] = 0
+                               }
+                       }
+                       if (err != nil) != tt.wantErr {
+                               t.Errorf("MarshalBinary() error = %v, wantErr %v", err, tt.wantErr)
+                               return
+                       }
+                       if !reflect.DeepEqual(gotData, tt.wantData) {
+                               t.Errorf("MarshalBinary() gotData = %v, want %v", gotData, tt.wantData)
+                       }
+               })
+       }
+}
index 1429c62f47e2bbeb5011db6f4e18ba8f0959d8ba..8e0fb5086056bcb0b3ba60e000bbaec867f409ce 100644 (file)
@@ -33,7 +33,6 @@ const (
 )
 
 type Server struct {
 )
 
 type Server struct {
-       Interface     string
        Port          int
        Accounts      map[string]*Account
        Agreement     []byte
        Port          int
        Accounts      map[string]*Account
        Agreement     []byte
@@ -115,13 +114,17 @@ func (s *Server) sendTransaction(t Transaction) error {
        if client == nil {
                return errors.New("invalid client")
        }
        if client == nil {
                return errors.New("invalid client")
        }
-       userName := string(*client.UserName)
+       userName := string(client.UserName)
        login := client.Account.Login
 
        handler := TransactionHandlers[requestNum]
 
        login := client.Account.Login
 
        handler := TransactionHandlers[requestNum]
 
+       b, err := t.MarshalBinary()
+       if err != nil {
+               return err
+       }
        var n int
        var n int
-       if n, err = client.Connection.Write(t.Payload()); err != nil {
+       if n, err = client.Connection.Write(b); err != nil {
                return err
        }
        s.Logger.Debugw("Sent Transaction",
                return err
        }
        s.Logger.Debugw("Sent Transaction",
@@ -168,8 +171,6 @@ func (s *Server) Serve(ctx context.Context, cancelRoot context.CancelFunc, ln ne
 
 const (
        agreementFile    = "Agreement.txt"
 
 const (
        agreementFile    = "Agreement.txt"
-       messageBoardFile = "MessageBoard.txt"
-       threadedNewsFile = "ThreadedNews.yaml"
 )
 
 // NewServer constructs a new Server from a config dir
 )
 
 // NewServer constructs a new Server from a config dir
@@ -290,7 +291,7 @@ func (s *Server) keepaliveHandler() {
                                        tranNotifyChangeUser,
                                        NewField(fieldUserID, *c.ID),
                                        NewField(fieldUserFlags, *c.Flags),
                                        tranNotifyChangeUser,
                                        NewField(fieldUserID, *c.ID),
                                        NewField(fieldUserFlags, *c.Flags),
-                                       NewField(fieldUserName, *c.UserName),
+                                       NewField(fieldUserName, c.UserName),
                                        NewField(fieldUserIconID, *c.Icon),
                                )
                        }
                                        NewField(fieldUserIconID, *c.Icon),
                                )
                        }
@@ -323,7 +324,7 @@ func (s *Server) NewClientConn(conn net.Conn) *ClientConn {
                ID:         &[]byte{0, 0},
                Icon:       &[]byte{0, 0},
                Flags:      &[]byte{0, 0},
                ID:         &[]byte{0, 0},
                Icon:       &[]byte{0, 0},
                Flags:      &[]byte{0, 0},
-               UserName:   &[]byte{},
+               UserName:   []byte{},
                Connection: conn,
                Server:     s,
                Version:    &[]byte{},
                Connection: conn,
                Server:     s,
                Version:    &[]byte{},
@@ -382,7 +383,7 @@ func (s *Server) connectedUsers() []Field {
                        ID:    *c.ID,
                        Icon:  *c.Icon,
                        Flags: *c.Flags,
                        ID:    *c.ID,
                        Icon:  *c.Icon,
                        Flags: *c.Flags,
-                       Name:  string(*c.UserName),
+                       Name:  string(c.UserName),
                }
                connectedUsers = append(connectedUsers, NewField(fieldUsernameWithInfo, user.Payload()))
        }
                }
                connectedUsers = append(connectedUsers, NewField(fieldUsernameWithInfo, user.Payload()))
        }
@@ -497,15 +498,19 @@ func (s *Server) handleNewConnection(conn net.Conn) error {
 
        // If authentication fails, send error reply and close connection
        if !c.Authenticate(login, encodedPassword) {
 
        // If authentication fails, send error reply and close connection
        if !c.Authenticate(login, encodedPassword) {
-               rep := c.NewErrReply(clientLogin, "Incorrect login.")
-               if _, err := conn.Write(rep.Payload()); err != nil {
+               t := c.NewErrReply(clientLogin, "Incorrect login.")
+               b, err := t.MarshalBinary()
+               if err != nil {
+                       return err
+               }
+               if _, err := conn.Write(b); err != nil {
                        return err
                }
                return fmt.Errorf("incorrect login")
        }
 
        if clientLogin.GetField(fieldUserName).Data != nil {
                        return err
                }
                return fmt.Errorf("incorrect login")
        }
 
        if clientLogin.GetField(fieldUserName).Data != nil {
-               *c.UserName = clientLogin.GetField(fieldUserName).Data
+               c.UserName = clientLogin.GetField(fieldUserName).Data
        }
 
        if clientLogin.GetField(fieldUserIconID).Data != nil {
        }
 
        if clientLogin.GetField(fieldUserIconID).Data != nil {
@@ -753,7 +758,8 @@ func (s *Server) TransferFile(conn net.Conn) error {
                //
                // This notifies the server to send the next item header
 
                //
                // This notifies the server to send the next item header
 
-               fh := NewFilePath(fileTransfer.FilePath)
+               var fh FilePath
+               _ = fh.UnmarshalBinary(fileTransfer.FilePath)
                fullFilePath := fmt.Sprintf("%v/%v", s.Config.FileRoot+fh.String(), string(fileTransfer.FileName))
 
                basePathLen := len(fullFilePath)
                fullFilePath := fmt.Sprintf("%v/%v", s.Config.FileRoot+fh.String(), string(fileTransfer.FileName))
 
                basePathLen := len(fullFilePath)
index 8fbdf35c0d652a71076c13fe776a625439c59c5c..835d194b24e5c445bcc741013c8e1f587125ab32 100644 (file)
@@ -214,7 +214,7 @@ func ReadFields(paramCount []byte, buf []byte) ([]Field, error) {
        return fields, nil
 }
 
        return fields, nil
 }
 
-func (t Transaction) Payload() []byte {
+func (t Transaction) MarshalBinary() (data []byte, err error) {
        payloadSize := t.Size()
 
        fieldCount := make([]byte, 2)
        payloadSize := t.Size()
 
        fieldCount := make([]byte, 2)
@@ -234,7 +234,7 @@ func (t Transaction) Payload() []byte {
                payloadSize, // this is the dataSize field, but seeming the same as totalSize
                fieldCount,
                fieldPayload,
                payloadSize, // this is the dataSize field, but seeming the same as totalSize
                fieldCount,
                fieldPayload,
-       )
+       ), err
 }
 
 // Size returns the total size of the transaction payload
 }
 
 // Size returns the total size of the transaction payload
index 5773d80375d5d871380f3501c355f28d1ab6e2ea..46f96ebc3f91f94b98d68091fd3174c428b084e2 100644 (file)
@@ -5,7 +5,6 @@ import (
        "encoding/binary"
        "errors"
        "fmt"
        "encoding/binary"
        "errors"
        "fmt"
-       "github.com/davecgh/go-spew/spew"
        "gopkg.in/yaml.v2"
        "io/ioutil"
        "math/big"
        "gopkg.in/yaml.v2"
        "io/ioutil"
        "math/big"
@@ -293,14 +292,14 @@ var TransactionHandlers = map[uint16]TransactionType{
 
 func HandleChatSend(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
        // Truncate long usernames
 
 func HandleChatSend(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
        // Truncate long usernames
-       trunc := fmt.Sprintf("%13s", *cc.UserName)
+       trunc := fmt.Sprintf("%13s", cc.UserName)
        formattedMsg := fmt.Sprintf("\r%.14s:  %s", trunc, t.GetField(fieldData).Data)
 
        // By holding the option key, Hotline chat allows users to send /me formatted messages like:
        // *** Halcyon does stuff
        // This is indicated by the presence of the optional field fieldChatOptions in the transaction payload
        if t.GetField(fieldChatOptions).Data != nil {
        formattedMsg := fmt.Sprintf("\r%.14s:  %s", trunc, t.GetField(fieldData).Data)
 
        // By holding the option key, Hotline chat allows users to send /me formatted messages like:
        // *** Halcyon does stuff
        // This is indicated by the presence of the optional field fieldChatOptions in the transaction payload
        if t.GetField(fieldChatOptions).Data != nil {
-               formattedMsg = fmt.Sprintf("\r*** %s %s", *cc.UserName, t.GetField(fieldData).Data)
+               formattedMsg = fmt.Sprintf("\r*** %s %s", cc.UserName, t.GetField(fieldData).Data)
        }
 
        if bytes.Equal(t.GetField(fieldData).Data, []byte("/stats")) {
        }
 
        if bytes.Equal(t.GetField(fieldData).Data, []byte("/stats")) {
@@ -360,7 +359,7 @@ func HandleSendInstantMsg(cc *ClientConn, t *Transaction) (res []Transaction, er
                        tranServerMsg,
                        &ID.Data,
                        NewField(fieldData, msg.Data),
                        tranServerMsg,
                        &ID.Data,
                        NewField(fieldData, msg.Data),
-                       NewField(fieldUserName, *cc.UserName),
+                       NewField(fieldUserName, cc.UserName),
                        NewField(fieldUserID, *cc.ID),
                        NewField(fieldOptions, []byte{0, 1}),
                ),
                        NewField(fieldUserID, *cc.ID),
                        NewField(fieldOptions, []byte{0, 1}),
                ),
@@ -384,7 +383,7 @@ func HandleSendInstantMsg(cc *ClientConn, t *Transaction) (res []Transaction, er
                                tranServerMsg,
                                cc.ID,
                                NewField(fieldData, *otherClient.AutoReply),
                                tranServerMsg,
                                cc.ID,
                                NewField(fieldData, *otherClient.AutoReply),
-                               NewField(fieldUserName, *otherClient.UserName),
+                               NewField(fieldUserName, otherClient.UserName),
                                NewField(fieldUserID, *otherClient.ID),
                                NewField(fieldOptions, []byte{0, 1}),
                        ),
                                NewField(fieldUserID, *otherClient.ID),
                                NewField(fieldOptions, []byte{0, 1}),
                        ),
@@ -399,7 +398,6 @@ func HandleSendInstantMsg(cc *ClientConn, t *Transaction) (res []Transaction, er
 func HandleGetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
        fileName := string(t.GetField(fieldFileName).Data)
        filePath := cc.Server.Config.FileRoot + ReadFilePath(t.GetField(fieldFilePath).Data)
 func HandleGetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
        fileName := string(t.GetField(fieldFileName).Data)
        filePath := cc.Server.Config.FileRoot + ReadFilePath(t.GetField(fieldFilePath).Data)
-       spew.Dump(cc.Server.Config.FileRoot)
 
        ffo, err := NewFlattenedFileObject(filePath, fileName)
        if err != nil {
 
        ffo, err := NewFlattenedFileObject(filePath, fileName)
        if err != nil {
@@ -547,7 +545,8 @@ func HandleNewFolder(cc *ClientConn, t *Transaction) (res []Transaction, err err
 
        // fieldFilePath is only present for nested paths
        if t.GetField(fieldFilePath).Data != nil {
 
        // fieldFilePath is only present for nested paths
        if t.GetField(fieldFilePath).Data != nil {
-               newFp := NewFilePath(t.GetField(fieldFilePath).Data)
+               var newFp FilePath
+               newFp.UnmarshalBinary(t.GetField(fieldFilePath).Data)
                newFolderPath += newFp.String()
        }
        newFolderPath += "/" + string(t.GetField(fieldFileName).Data)
                newFolderPath += newFp.String()
        }
        newFolderPath += "/" + string(t.GetField(fieldFileName).Data)
@@ -610,7 +609,7 @@ func HandleSetUser(cc *ClientConn, t *Transaction) (res []Transaction, err error
                                tranNotifyChangeUser,
                                NewField(fieldUserID, *c.ID),
                                NewField(fieldUserFlags, *c.Flags),
                                tranNotifyChangeUser,
                                NewField(fieldUserID, *c.ID),
                                NewField(fieldUserFlags, *c.Flags),
-                               NewField(fieldUserName, *c.UserName),
+                               NewField(fieldUserName, c.UserName),
                                NewField(fieldUserIconID, *c.Icon),
                        )
                }
                                NewField(fieldUserIconID, *c.Icon),
                        )
                }
@@ -756,7 +755,7 @@ None.
 
        template = fmt.Sprintf(
                template,
 
        template = fmt.Sprintf(
                template,
-               *clientConn.UserName,
+               clientConn.UserName,
                clientConn.Account.Name,
                clientConn.Account.Login,
                clientConn.Connection.RemoteAddr().String(),
                clientConn.Account.Name,
                clientConn.Account.Login,
                clientConn.Connection.RemoteAddr().String(),
@@ -766,7 +765,7 @@ None.
 
        res = append(res, cc.NewReply(t,
                NewField(fieldData, []byte(template)),
 
        res = append(res, cc.NewReply(t,
                NewField(fieldData, []byte(template)),
-               NewField(fieldUserName, *clientConn.UserName),
+               NewField(fieldUserName, clientConn.UserName),
        ))
        return res, err
 }
        ))
        return res, err
 }
@@ -782,7 +781,7 @@ func (cc *ClientConn) notifyNewUserHasJoined() (res []Transaction, err error) {
        cc.NotifyOthers(
                *NewTransaction(
                        tranNotifyChangeUser, nil,
        cc.NotifyOthers(
                *NewTransaction(
                        tranNotifyChangeUser, nil,
-                       NewField(fieldUserName, *cc.UserName),
+                       NewField(fieldUserName, cc.UserName),
                        NewField(fieldUserID, *cc.ID),
                        NewField(fieldUserIconID, *cc.Icon),
                        NewField(fieldUserFlags, *cc.Flags),
                        NewField(fieldUserID, *cc.ID),
                        NewField(fieldUserIconID, *cc.Icon),
                        NewField(fieldUserFlags, *cc.Flags),
@@ -796,7 +795,7 @@ func HandleTranAgreed(cc *ClientConn, t *Transaction) (res []Transaction, err er
        bs := make([]byte, 2)
        binary.BigEndian.PutUint16(bs, *cc.Server.NextGuestID)
 
        bs := make([]byte, 2)
        binary.BigEndian.PutUint16(bs, *cc.Server.NextGuestID)
 
-       *cc.UserName = t.GetField(fieldUserName).Data
+       cc.UserName = t.GetField(fieldUserName).Data
        *cc.ID = bs
        *cc.Icon = t.GetField(fieldUserIconID).Data
 
        *cc.ID = bs
        *cc.Icon = t.GetField(fieldUserIconID).Data
 
@@ -857,7 +856,7 @@ func HandleTranOldPostNews(cc *ClientConn, t *Transaction) (res []Transaction, e
                newsTemplate = cc.Server.Config.NewsDelimiter
        }
 
                newsTemplate = cc.Server.Config.NewsDelimiter
        }
 
-       newsPost := fmt.Sprintf(newsTemplate+"\r", *cc.UserName, time.Now().Format(newsDateTemplate), t.GetField(fieldData).Data)
+       newsPost := fmt.Sprintf(newsTemplate+"\r", cc.UserName, time.Now().Format(newsDateTemplate), t.GetField(fieldData).Data)
        newsPost = strings.Replace(newsPost, "\n", "\r", -1)
 
        // update news in memory
        newsPost = strings.Replace(newsPost, "\n", "\r", -1)
 
        // update news in memory
@@ -916,9 +915,10 @@ func HandleGetNewsCatNameList(cc *ClientConn, t *Transaction) (res []Transaction
        var fieldData []Field
        for _, k := range keys {
                cat := cats[k]
        var fieldData []Field
        for _, k := range keys {
                cat := cats[k]
+               b, _ := cat.MarshalBinary()
                fieldData = append(fieldData, NewField(
                        fieldNewsCatListData15,
                fieldData = append(fieldData, NewField(
                        fieldNewsCatListData15,
-                       cat.Payload(),
+                       b,
                ))
        }
 
                ))
        }
 
@@ -1113,7 +1113,7 @@ func HandlePostNewsArt(cc *ClientConn, t *Transaction) (res []Transaction, err e
 
        newArt := NewsArtData{
                Title:         string(t.GetField(fieldNewsArtTitle).Data),
 
        newArt := NewsArtData{
                Title:         string(t.GetField(fieldNewsArtTitle).Data),
-               Poster:        string(*cc.UserName),
+               Poster:        string(cc.UserName),
                Date:          NewsDate(),
                PrevArt:       []byte{0, 0, 0, 0},
                NextArt:       []byte{0, 0, 0, 0},
                Date:          NewsDate(),
                PrevArt:       []byte{0, 0, 0, 0},
                NextArt:       []byte{0, 0, 0, 0},
@@ -1239,7 +1239,8 @@ func HandleDownloadFolder(cc *ClientConn, t *Transaction) (res []Transaction, er
        cc.Server.FileTransfers[data] = fileTransfer
        cc.Transfers[FolderDownload] = append(cc.Transfers[FolderDownload], fileTransfer)
 
        cc.Server.FileTransfers[data] = fileTransfer
        cc.Transfers[FolderDownload] = append(cc.Transfers[FolderDownload], fileTransfer)
 
-       fp := NewFilePath(t.GetField(fieldFilePath).Data)
+       var fp FilePath
+       fp.UnmarshalBinary(t.GetField(fieldFilePath).Data)
 
        fullFilePath := fmt.Sprintf("%v%v", cc.Server.Config.FileRoot+fp.String(), string(fileTransfer.FileName))
        transferSize, err := CalcTotalSize(fullFilePath)
 
        fullFilePath := fmt.Sprintf("%v%v", cc.Server.Config.FileRoot+fp.String(), string(fileTransfer.FileName))
        transferSize, err := CalcTotalSize(fullFilePath)
@@ -1319,7 +1320,7 @@ func HandleSetClientUserInfo(cc *ClientConn, t *Transaction) (res []Transaction,
                icon = t.GetField(fieldUserIconID).Data
        }
        *cc.Icon = icon
                icon = t.GetField(fieldUserIconID).Data
        }
        *cc.Icon = icon
-       *cc.UserName = t.GetField(fieldUserName).Data
+       cc.UserName = t.GetField(fieldUserName).Data
 
        // the options field is only passed by the client versions > 1.2.3.
        options := t.GetField(fieldOptions).Data
 
        // the options field is only passed by the client versions > 1.2.3.
        options := t.GetField(fieldOptions).Data
@@ -1354,7 +1355,7 @@ func HandleSetClientUserInfo(cc *ClientConn, t *Transaction) (res []Transaction,
                NewField(fieldUserID, *cc.ID),
                NewField(fieldUserIconID, *cc.Icon),
                NewField(fieldUserFlags, *cc.Flags),
                NewField(fieldUserID, *cc.ID),
                NewField(fieldUserIconID, *cc.Icon),
                NewField(fieldUserFlags, *cc.Flags),
-               NewField(fieldUserName, *cc.UserName),
+               NewField(fieldUserName, cc.UserName),
        )
 
        return res, err
        )
 
        return res, err
@@ -1410,7 +1411,7 @@ func HandleInviteNewChat(cc *ClientConn, t *Transaction) (res []Transaction, err
                        tranInviteToChat,
                        &targetID,
                        NewField(fieldChatID, newChatID),
                        tranInviteToChat,
                        &targetID,
                        NewField(fieldChatID, newChatID),
-                       NewField(fieldUserName, *cc.UserName),
+                       NewField(fieldUserName, cc.UserName),
                        NewField(fieldUserID, *cc.ID),
                ),
        )
                        NewField(fieldUserID, *cc.ID),
                ),
        )
@@ -1418,7 +1419,7 @@ func HandleInviteNewChat(cc *ClientConn, t *Transaction) (res []Transaction, err
        res = append(res,
                cc.NewReply(t,
                        NewField(fieldChatID, newChatID),
        res = append(res,
                cc.NewReply(t,
                        NewField(fieldChatID, newChatID),
-                       NewField(fieldUserName, *cc.UserName),
+                       NewField(fieldUserName, cc.UserName),
                        NewField(fieldUserID, *cc.ID),
                        NewField(fieldUserIconID, *cc.Icon),
                        NewField(fieldUserFlags, *cc.Flags),
                        NewField(fieldUserID, *cc.ID),
                        NewField(fieldUserIconID, *cc.Icon),
                        NewField(fieldUserFlags, *cc.Flags),
@@ -1438,7 +1439,7 @@ func HandleInviteToChat(cc *ClientConn, t *Transaction) (res []Transaction, err
                        tranInviteToChat,
                        &targetID,
                        NewField(fieldChatID, chatID),
                        tranInviteToChat,
                        &targetID,
                        NewField(fieldChatID, chatID),
-                       NewField(fieldUserName, *cc.UserName),
+                       NewField(fieldUserName, cc.UserName),
                        NewField(fieldUserID, *cc.ID),
                ),
        )
                        NewField(fieldUserID, *cc.ID),
                ),
        )
@@ -1446,7 +1447,7 @@ func HandleInviteToChat(cc *ClientConn, t *Transaction) (res []Transaction, err
                cc.NewReply(
                        t,
                        NewField(fieldChatID, chatID),
                cc.NewReply(
                        t,
                        NewField(fieldChatID, chatID),
-                       NewField(fieldUserName, *cc.UserName),
+                       NewField(fieldUserName, cc.UserName),
                        NewField(fieldUserID, *cc.ID),
                        NewField(fieldUserIconID, *cc.Icon),
                        NewField(fieldUserFlags, *cc.Flags),
                        NewField(fieldUserID, *cc.ID),
                        NewField(fieldUserIconID, *cc.Icon),
                        NewField(fieldUserFlags, *cc.Flags),
@@ -1462,7 +1463,7 @@ func HandleRejectChatInvite(cc *ClientConn, t *Transaction) (res []Transaction,
 
        privChat := cc.Server.PrivateChats[chatInt]
 
 
        privChat := cc.Server.PrivateChats[chatInt]
 
-       resMsg := append(*cc.UserName, []byte(" declined invitation to chat")...)
+       resMsg := append(cc.UserName, []byte(" declined invitation to chat")...)
 
        for _, c := range sortedClients(privChat.ClientConn) {
                res = append(res,
 
        for _, c := range sortedClients(privChat.ClientConn) {
                res = append(res,
@@ -1496,7 +1497,7 @@ func HandleJoinChat(cc *ClientConn, t *Transaction) (res []Transaction, err erro
                                tranNotifyChatChangeUser,
                                c.ID,
                                NewField(fieldChatID, chatID),
                                tranNotifyChatChangeUser,
                                c.ID,
                                NewField(fieldChatID, chatID),
-                               NewField(fieldUserName, *cc.UserName),
+                               NewField(fieldUserName, cc.UserName),
                                NewField(fieldUserID, *cc.ID),
                                NewField(fieldUserIconID, *cc.Icon),
                                NewField(fieldUserFlags, *cc.Flags),
                                NewField(fieldUserID, *cc.ID),
                                NewField(fieldUserIconID, *cc.Icon),
                                NewField(fieldUserFlags, *cc.Flags),
@@ -1512,7 +1513,7 @@ func HandleJoinChat(cc *ClientConn, t *Transaction) (res []Transaction, err erro
                        ID:    *c.ID,
                        Icon:  *c.Icon,
                        Flags: *c.Flags,
                        ID:    *c.ID,
                        Icon:  *c.Icon,
                        Flags: *c.Flags,
-                       Name:  string(*c.UserName),
+                       Name:  string(c.UserName),
                }
 
                replyFields = append(replyFields, NewField(fieldUsernameWithInfo, user.Payload()))
                }
 
                replyFields = append(replyFields, NewField(fieldUsernameWithInfo, user.Payload()))
index cf05a99f36af0d5e86d93f05a97ba5cc4524877f..e62cce20176ae808bdf6f04ac8eaa659bd7f8414 100644 (file)
@@ -22,7 +22,7 @@ func TestHandleSetChatSubject(t *testing.T) {
                        name: "sends chat subject to private chat members",
                        args: args{
                                cc: &ClientConn{
                        name: "sends chat subject to private chat members",
                        args: args{
                                cc: &ClientConn{
-                                       UserName: &[]byte{0x00, 0x01},
+                                       UserName: []byte{0x00, 0x01},
                                        Server: &Server{
                                                PrivateChats: map[uint32]*PrivateChat{
                                                        uint32(1): {
                                        Server: &Server{
                                                PrivateChats: map[uint32]*PrivateChat{
                                                        uint32(1): {
@@ -224,7 +224,7 @@ func TestHandleGetUserNameList(t *testing.T) {
                                                                ID:       &[]byte{0, 1},
                                                                Icon:     &[]byte{0, 2},
                                                                Flags:    &[]byte{0, 3},
                                                                ID:       &[]byte{0, 1},
                                                                Icon:     &[]byte{0, 2},
                                                                Flags:    &[]byte{0, 3},
-                                                               UserName: &[]byte{0, 4},
+                                                               UserName: []byte{0, 4},
                                                        },
                                                },
                                        },
                                                        },
                                                },
                                        },
@@ -282,7 +282,7 @@ func TestHandleChatSend(t *testing.T) {
                        name: "sends chat msg transaction to all clients",
                        args: args{
                                cc: &ClientConn{
                        name: "sends chat msg transaction to all clients",
                        args: args{
                                cc: &ClientConn{
-                                       UserName: &[]byte{0x00, 0x01},
+                                       UserName: []byte{0x00, 0x01},
                                        Server: &Server{
                                                Clients: map[uint16]*ClientConn{
                                                        uint16(1): {
                                        Server: &Server{
                                                Clients: map[uint16]*ClientConn{
                                                        uint16(1): {
@@ -332,11 +332,66 @@ func TestHandleChatSend(t *testing.T) {
                        },
                        wantErr: false,
                },
                        },
                        wantErr: false,
                },
+               {
+                       name: "sends chat msg as emote if fieldChatOptions is set",
+                       args: args{
+                               cc: &ClientConn{
+                                       UserName: []byte("Testy McTest"),
+                                       Server: &Server{
+                                               Clients: map[uint16]*ClientConn{
+                                                       uint16(1): {
+                                                               Account: &Account{
+                                                                       Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+                                                               },
+                                                               ID: &[]byte{0, 1},
+                                                       },
+                                                       uint16(2): {
+                                                               Account: &Account{
+                                                                       Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+                                                               },
+                                                               ID: &[]byte{0, 2},
+                                                       },
+                                               },
+                                       },
+                               },
+                               t: &Transaction{
+                                       Fields: []Field{
+                                               NewField(fieldData, []byte("performed action")),
+                                               NewField(fieldChatOptions, []byte{0x00, 0x01}),
+                                       },
+                               },
+                       },
+                       want: []Transaction{
+                               {
+                                       clientID:  &[]byte{0, 1},
+                                       Flags:     0x00,
+                                       IsReply:   0x00,
+                                       Type:      []byte{0, 0x6a},
+                                       ID:        []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
+                                       ErrorCode: []byte{0, 0, 0, 0},
+                                       Fields: []Field{
+                                               NewField(fieldData, []byte("\r*** Testy McTest performed action")),
+                                       },
+                               },
+                               {
+                                       clientID:  &[]byte{0, 2},
+                                       Flags:     0x00,
+                                       IsReply:   0x00,
+                                       Type:      []byte{0, 0x6a},
+                                       ID:        []byte{0xf0, 0xc5, 0x34, 0x1e}, // Random ID from rand.Seed(1)
+                                       ErrorCode: []byte{0, 0, 0, 0},
+                                       Fields: []Field{
+                                               NewField(fieldData, []byte("\r*** Testy McTest performed action")),
+                                       },
+                               },
+                       },
+                       wantErr: false,
+               },
                {
                        name: "only sends chat msg to clients with accessReadChat permission",
                        args: args{
                                cc: &ClientConn{
                {
                        name: "only sends chat msg to clients with accessReadChat permission",
                        args: args{
                                cc: &ClientConn{
-                                       UserName: &[]byte{0x00, 0x01},
+                                       UserName: []byte{0x00, 0x01},
                                        Server: &Server{
                                                Clients: map[uint16]*ClientConn{
                                                        uint16(1): {
                                        Server: &Server{
                                                Clients: map[uint16]*ClientConn{
                                                        uint16(1): {
@@ -391,3 +446,85 @@ func TestHandleChatSend(t *testing.T) {
                })
        }
 }
                })
        }
 }
+
+func TestHandleGetFileInfo(t *testing.T) {
+       rand.Seed(1) // reset seed between tests to make transaction IDs predictable
+
+       type args struct {
+               cc *ClientConn
+               t  *Transaction
+       }
+       tests := []struct {
+               name    string
+               args    args
+               wantRes []Transaction
+               wantErr bool
+       }{
+               {
+                       name: "returns expected fields when a valid file is requested",
+                       args: args{
+                               cc: &ClientConn{
+                                       ID: &[]byte{0x00, 0x01},
+                                       Server: &Server{
+                                               Config: &Config{
+                                                       FileRoot: "./test/config/Files/",
+                                               },
+                                       },
+                               },
+                               t: NewTransaction(
+                                       tranGetFileInfo, nil,
+                                       NewField(fieldFileName, []byte("testfile.txt")),
+                                       NewField(fieldFilePath, []byte{0x00, 0x00}),
+                                       //NewField(fieldFilePath, []byte{
+                                       //      0x00, 0x03,
+                                       //      0x00, 0x00,
+                                       //      0x04,
+                                       //      0x74, 0x65, 0x73, 0x74,
+                                       //      0x00, 0x00,
+                                       //      0x06,
+                                       //      0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+                                       //
+                                       //      0x00, 0x00,
+                                       //      0x05,
+                                       //      0x46, 0x69, 0x6c, 0x65, 73},
+                                       //),
+                               ),
+                       },
+                       wantRes: []Transaction{
+                               {
+                                       clientID:  &[]byte{0, 1},
+                                       Flags:     0x00,
+                                       IsReply:   0x01,
+                                       Type:      []byte{0, 0xce},
+                                       ID:        []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
+                                       ErrorCode: []byte{0, 0, 0, 0},
+                                       Fields: []Field{
+                                               NewField(fieldFileName, []byte("testfile.txt")),
+                                               NewField(fieldFileTypeString, []byte("TEXT")),
+                                               NewField(fieldFileCreatorString, []byte("TTXT")),
+                                               NewField(fieldFileComment, []byte("TODO")),
+                                               NewField(fieldFileType, []byte("TEXT")),
+                                               NewField(fieldFileCreateDate, []byte{0x07, 0x70, 0x00, 0x00, 0xba, 0x74, 0x24, 0x73}),
+                                               NewField(fieldFileModifyDate, []byte{0x07, 0x70, 0x00, 0x00, 0xba, 0x74, 0x24, 0x73}),
+                                               NewField(fieldFileSize, []byte{0x0, 0x0, 0x0, 0x17}),
+                                       },
+                               },
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       rand.Seed(1) // reset seed between tests to make transaction IDs predictable
+
+                       gotRes, err := HandleGetFileInfo(tt.args.cc, tt.args.t)
+                       if (err != nil) != tt.wantErr {
+                               t.Errorf("HandleGetFileInfo() error = %v, wantErr %v", err, tt.wantErr)
+                               return
+                       }
+                       if !assert.Equal(t, tt.wantRes,  gotRes) {
+                               t.Errorf("HandleGetFileInfo() gotRes = %v, want %v", gotRes, tt.wantRes)
+                       }
+               })
+       }
+}
index 4c7d3725168b54f9c39719158fc53e8a5490da7e..1fade255c63bc5e2047624b2d459b7257e93b066 100644 (file)
@@ -142,16 +142,25 @@ func TestReadTransaction(t *testing.T) {
                {
                        name: "when buf contains all bytes for a single transaction",
                        args: args{
                {
                        name: "when buf contains all bytes for a single transaction",
                        args: args{
-                               buf: sampleTransaction.Payload(),
+                               buf: func() []byte {
+                                       b, _ := sampleTransaction.MarshalBinary()
+                                       return b
+                               }(),
                        },
                        want:    sampleTransaction,
                        },
                        want:    sampleTransaction,
-                       want1:   len(sampleTransaction.Payload()),
+                       want1:   func() int {
+                               b, _ := sampleTransaction.MarshalBinary()
+                               return len(b)
+                       }(),
                        wantErr: false,
                },
                {
                        name: "when len(buf) is less than the length of the transaction",
                        args: args{
                        wantErr: false,
                },
                {
                        name: "when len(buf) is less than the length of the transaction",
                        args: args{
-                               buf: sampleTransaction.Payload()[:len(sampleTransaction.Payload())-1],
+                               buf: func() []byte {
+                                       b, _ := sampleTransaction.MarshalBinary()
+                                       return b[:len(b)-1]
+                               }(),
                        },
                        want:    nil,
                        want1:   0,
                        },
                        want:    nil,
                        want1:   0,
index 3d0fa68a941c138b3d0b9f91ec0e4434e1ac22af..d0fd6c04a999866fffc0d7743bd4f4967650e099 100644 (file)
@@ -19,7 +19,6 @@ type UI struct {
        App         *tview.Application
        Pages       *tview.Pages
        userList    *tview.TextView
        App         *tview.Application
        Pages       *tview.Pages
        userList    *tview.TextView
-       agreeModal  *tview.Modal
        trackerList *tview.List
        HLClient    *Client
 }
        trackerList *tview.List
        HLClient    *Client
 }
@@ -75,7 +74,6 @@ func NewUI(c *Client) *UI {
                chatInput:   chatInput,
                userList:    userList,
                trackerList: tview.NewList(),
                chatInput:   chatInput,
                userList:    userList,
                trackerList: tview.NewList(),
-               agreeModal:  tview.NewModal(),
                HLClient:    c,
        }
 }
                HLClient:    c,
        }
 }