From: Jeff Halter Date: Tue, 18 Jun 2024 22:34:50 +0000 (-0700) Subject: Adopt more fixed size array types for struct fields X-Git-Url: https://git.r.bdr.sh/rbdr/mobius/commitdiff_plain/a55350daaf83498b7a237c027ad0dd2377f06fee Adopt more fixed size array types for struct fields --- diff --git a/hotline/field.go b/hotline/field.go index 045e09f..2fcb41a 100644 --- a/hotline/field.go +++ b/hotline/field.go @@ -80,17 +80,11 @@ type requiredField struct { } func NewField(id uint16, data []byte) Field { - idBytes := make([]byte, 2) - binary.BigEndian.PutUint16(idBytes, id) + f := Field{Data: data} + binary.BigEndian.PutUint16(f.ID[:], id) + binary.BigEndian.PutUint16(f.FieldSize[:], uint16(len(data))) - bs := make([]byte, 2) - binary.BigEndian.PutUint16(bs, uint16(len(data))) - - return Field{ - ID: [2]byte(idBytes), - FieldSize: [2]byte(bs), - Data: data, - } + return f } // fieldScanner implements bufio.SplitFunc for parsing byte slices into complete tokens diff --git a/hotline/field_test.go b/hotline/field_test.go index 317c2d9..ecae9c7 100644 --- a/hotline/field_test.go +++ b/hotline/field_test.go @@ -99,3 +99,90 @@ func Test_fieldScanner(t *testing.T) { }) } } + +func TestField_Read(t *testing.T) { + type fields struct { + ID [2]byte + FieldSize [2]byte + Data []byte + readOffset int + } + type args struct { + p []byte + } + tests := []struct { + name string + fields fields + args args + want int + wantErr assert.ErrorAssertionFunc + wantBytes []byte + }{ + { + name: "returns field bytes", + fields: fields{ + ID: [2]byte{0x00, 0x62}, + FieldSize: [2]byte{0x00, 0x03}, + Data: []byte("hai!"), + }, + args: args{ + p: make([]byte, 512), + }, + want: 8, + wantErr: assert.NoError, + wantBytes: []byte{ + 0x00, 0x62, + 0x00, 0x03, + 0x68, 0x61, 0x69, 0x21, + }, + }, + { + name: "returns field bytes from readOffset", + fields: fields{ + ID: [2]byte{0x00, 0x62}, + FieldSize: [2]byte{0x00, 0x03}, + Data: []byte("hai!"), + readOffset: 4, + }, + args: args{ + p: make([]byte, 512), + }, + want: 4, + wantErr: assert.NoError, + wantBytes: []byte{ + 0x68, 0x61, 0x69, 0x21, + }, + }, + { + name: "returns io.EOF when all bytes read", + fields: fields{ + ID: [2]byte{0x00, 0x62}, + FieldSize: [2]byte{0x00, 0x03}, + Data: []byte("hai!"), + readOffset: 8, + }, + args: args{ + p: make([]byte, 512), + }, + want: 0, + wantErr: assert.Error, + wantBytes: []byte{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f := &Field{ + ID: tt.fields.ID, + FieldSize: tt.fields.FieldSize, + Data: tt.fields.Data, + readOffset: tt.fields.readOffset, + } + got, err := f.Read(tt.args.p) + if !tt.wantErr(t, err, fmt.Sprintf("Read(%v)", tt.args.p)) { + return + } + assert.Equalf(t, tt.want, got, "Read(%v)", tt.args.p) + assert.Equalf(t, tt.wantBytes, tt.args.p[:got], "Read(%v)", tt.args.p) + }) + } +} diff --git a/hotline/file_wrapper.go b/hotline/file_wrapper.go index 342355b..bc6319b 100644 --- a/hotline/file_wrapper.go +++ b/hotline/file_wrapper.go @@ -248,24 +248,21 @@ func (f *fileWrapper) flattenedFileObject() (*flattenedFileObject, error) { if err != nil { return nil, fmt.Errorf("error copying FlatFileInformationFork: %w", err) } - } else { f.ffo.FlatFileInformationFork = FlatFileInformationFork{ - Platform: []byte("AMAC"), // TODO: Remove hardcode to support "AWIN" Platform (maybe?) - TypeSignature: []byte(ft.TypeCode), - CreatorSignature: []byte(ft.CreatorCode), - Flags: []byte{0, 0, 0, 0}, - PlatformFlags: []byte{0, 0, 1, 0}, // TODO: What is this? - RSVD: make([]byte, 32), - CreateDate: mTime[:], // some filesystems don't support createTime - ModifyDate: mTime[:], - NameScript: []byte{0, 0}, + Platform: [4]byte{0x41, 0x4D, 0x41, 0x43}, // "AMAC" TODO: Remove hardcode to support "AWIN" Platform (maybe?) + TypeSignature: [4]byte([]byte(ft.TypeCode)), + CreatorSignature: [4]byte([]byte(ft.CreatorCode)), + PlatformFlags: [4]byte{0, 0, 1, 0}, // TODO: What is this? + CreateDate: mTime, // some filesystems don't support createTime + ModifyDate: mTime, Name: []byte(f.name), - NameSize: []byte{0, 0}, - CommentSize: []byte{0, 0}, Comment: []byte{}, } - binary.BigEndian.PutUint16(f.ffo.FlatFileInformationFork.NameSize, uint16(len(f.name))) + + ns := make([]byte, 2) + binary.BigEndian.PutUint16(ns, uint16(len(f.name))) + f.ffo.FlatFileInformationFork.NameSize = [2]byte(ns[:]) } f.ffo.FlatFileInformationForkHeader = FlatFileForkHeader{ diff --git a/hotline/files.go b/hotline/files.go index 6753cce..f2fc171 100644 --- a/hotline/files.go +++ b/hotline/files.go @@ -118,8 +118,8 @@ func getFileNameList(path string, ignoreList []string) (fields []Field, err erro } copy(fnwi.FileSize[:], hlFile.totalSize()) - copy(fnwi.Type[:], hlFile.ffo.FlatFileInformationFork.TypeSignature) - copy(fnwi.Creator[:], hlFile.ffo.FlatFileInformationFork.CreatorSignature) + copy(fnwi.Type[:], hlFile.ffo.FlatFileInformationFork.TypeSignature[:]) + copy(fnwi.Creator[:], hlFile.ffo.FlatFileInformationFork.CreatorSignature[:]) } strippedName := strings.ReplaceAll(file.Name(), ".incomplete", "") diff --git a/hotline/flattened_file_object.go b/hotline/flattened_file_object.go index b178c6c..15ce94d 100644 --- a/hotline/flattened_file_object.go +++ b/hotline/flattened_file_object.go @@ -26,59 +26,55 @@ type FlatFileHeader struct { } type FlatFileInformationFork struct { - Platform []byte // Operating System used. ("AMAC" or "MWIN") - TypeSignature []byte // File type signature - CreatorSignature []byte // File creator signature - Flags []byte - PlatformFlags []byte - RSVD []byte - CreateDate []byte - ModifyDate []byte - NameScript []byte - NameSize []byte // Length of file name (Maximum 128 characters) - Name []byte // File name - CommentSize []byte // Length of the comment - Comment []byte // File comment + Platform [4]byte // Operating System used. ("AMAC" or "MWIN") + TypeSignature [4]byte // File type signature + CreatorSignature [4]byte // File creator signature + Flags [4]byte + PlatformFlags [4]byte + RSVD [32]byte + CreateDate [8]byte + ModifyDate [8]byte + NameScript [2]byte + NameSize [2]byte // Length of file name (Maximum 128 characters) + Name []byte // File name + CommentSize [2]byte // Length of the comment + Comment []byte // File comment readOffset int // Internal offset to track read progress } -func NewFlatFileInformationFork(fileName string, modifyTime []byte, typeSignature string, creatorSignature string) FlatFileInformationFork { +func NewFlatFileInformationFork(fileName string, modifyTime [8]byte, typeSignature string, creatorSignature string) FlatFileInformationFork { return FlatFileInformationFork{ - Platform: []byte("AMAC"), // TODO: Remove hardcode to support "AWIN" Platform (maybe?) - TypeSignature: []byte(typeSignature), // TODO: Don't infer types from filename - CreatorSignature: []byte(creatorSignature), // TODO: Don't infer types from filename - Flags: []byte{0, 0, 0, 0}, // TODO: What is this? - PlatformFlags: []byte{0, 0, 1, 0}, // TODO: What is this? - RSVD: make([]byte, 32), // Unimplemented in Hotline Protocol - CreateDate: modifyTime, // some filesystems don't support createTime + Platform: [4]byte{0x41, 0x4D, 0x41, 0x43}, // "AMAC" TODO: Remove hardcode to support "AWIN" Platform (maybe?) + TypeSignature: [4]byte([]byte(typeSignature)), // TODO: Don't infer types from filename + CreatorSignature: [4]byte([]byte(creatorSignature)), // TODO: Don't infer types from filename + PlatformFlags: [4]byte{0, 0, 1, 0}, // TODO: What is this? + CreateDate: modifyTime, // some filesystems don't support createTime ModifyDate: modifyTime, - NameScript: make([]byte, 2), // TODO: What is this? Name: []byte(fileName), - CommentSize: []byte{0, 0}, Comment: []byte{}, // TODO: implement (maybe?) } } func (ffif *FlatFileInformationFork) friendlyType() []byte { - if name, ok := friendlyCreatorNames[string(ffif.TypeSignature)]; ok { + if name, ok := friendlyCreatorNames[string(ffif.TypeSignature[:])]; ok { return []byte(name) } - return ffif.TypeSignature + return ffif.TypeSignature[:] } func (ffif *FlatFileInformationFork) friendlyCreator() []byte { - if name, ok := friendlyCreatorNames[string(ffif.CreatorSignature)]; ok { + if name, ok := friendlyCreatorNames[string(ffif.CreatorSignature[:])]; ok { return []byte(name) } - return ffif.CreatorSignature + return ffif.CreatorSignature[:] } func (ffif *FlatFileInformationFork) setComment(comment []byte) error { - ffif.CommentSize = make([]byte, 2) + commentSize := make([]byte, 2) ffif.Comment = comment - binary.BigEndian.PutUint16(ffif.CommentSize, uint16(len(comment))) - + binary.BigEndian.PutUint16(commentSize, uint16(len(comment))) + ffif.CommentSize = [2]byte(commentSize) // TODO: return err if comment is too long return nil } @@ -138,18 +134,18 @@ type FlatFileForkHeader struct { func (ffif *FlatFileInformationFork) Read(p []byte) (int, error) { buf := slices.Concat( - ffif.Platform, - ffif.TypeSignature, - ffif.CreatorSignature, - ffif.Flags, - ffif.PlatformFlags, - ffif.RSVD, - ffif.CreateDate, - ffif.ModifyDate, - ffif.NameScript, + ffif.Platform[:], + ffif.TypeSignature[:], + ffif.CreatorSignature[:], + ffif.Flags[:], + ffif.PlatformFlags[:], + ffif.RSVD[:], + ffif.CreateDate[:], + ffif.ModifyDate[:], + ffif.NameScript[:], ffif.ReadNameSize(), ffif.Name, - ffif.CommentSize, + ffif.CommentSize[:], ffif.Comment, ) @@ -169,21 +165,21 @@ func (ffif *FlatFileInformationFork) Write(p []byte) (int, error) { bs := binary.BigEndian.Uint16(nameSize) total := 72 + bs - ffif.Platform = p[0:4] - ffif.TypeSignature = p[4:8] - ffif.CreatorSignature = p[8:12] - ffif.Flags = p[12:16] - ffif.PlatformFlags = p[16:20] - ffif.RSVD = p[20:52] - ffif.CreateDate = p[52:60] - ffif.ModifyDate = p[60:68] - ffif.NameScript = p[68:70] - ffif.NameSize = p[70:72] + ffif.Platform = [4]byte(p[0:4]) + ffif.TypeSignature = [4]byte(p[4:8]) + ffif.CreatorSignature = [4]byte(p[8:12]) + ffif.Flags = [4]byte(p[12:16]) + ffif.PlatformFlags = [4]byte(p[16:20]) + ffif.RSVD = [32]byte(p[20:52]) + ffif.CreateDate = [8]byte(p[52:60]) + ffif.ModifyDate = [8]byte(p[60:68]) + ffif.NameScript = [2]byte(p[68:70]) + ffif.NameSize = [2]byte(p[70:72]) ffif.Name = p[72:total] if len(p) > int(total) { - ffif.CommentSize = p[total : total+2] - commentLen := binary.BigEndian.Uint16(ffif.CommentSize) + ffif.CommentSize = [2]byte(p[total : total+2]) + commentLen := binary.BigEndian.Uint16(ffif.CommentSize[:]) commentStartPos := int(total) + 2 commentEndPos := int(total) + 2 + int(commentLen) @@ -200,21 +196,21 @@ func (ffif *FlatFileInformationFork) UnmarshalBinary(b []byte) error { bs := binary.BigEndian.Uint16(nameSize) nameEnd := 72 + bs - ffif.Platform = b[0:4] - ffif.TypeSignature = b[4:8] - ffif.CreatorSignature = b[8:12] - ffif.Flags = b[12:16] - ffif.PlatformFlags = b[16:20] - ffif.RSVD = b[20:52] - ffif.CreateDate = b[52:60] - ffif.ModifyDate = b[60:68] - ffif.NameScript = b[68:70] - ffif.NameSize = b[70:72] + ffif.Platform = [4]byte(b[0:4]) + ffif.TypeSignature = [4]byte(b[4:8]) + ffif.CreatorSignature = [4]byte(b[8:12]) + ffif.Flags = [4]byte(b[12:16]) + ffif.PlatformFlags = [4]byte(b[16:20]) + ffif.RSVD = [32]byte(b[20:52]) + ffif.CreateDate = [8]byte(b[52:60]) + ffif.ModifyDate = [8]byte(b[60:68]) + ffif.NameScript = [2]byte(b[68:70]) + ffif.NameSize = [2]byte(b[70:72]) ffif.Name = b[72:nameEnd] if len(b) > int(nameEnd) { - ffif.CommentSize = b[nameEnd : nameEnd+2] - commentLen := binary.BigEndian.Uint16(ffif.CommentSize) + ffif.CommentSize = [2]byte(b[nameEnd : nameEnd+2]) + commentLen := binary.BigEndian.Uint16(ffif.CommentSize[:]) commentStartPos := int(nameEnd) + 2 commentEndPos := int(nameEnd) + 2 + int(commentLen) @@ -236,18 +232,18 @@ func (ffo *flattenedFileObject) Read(p []byte) (int, error) { []byte{0, 0, 0, 0}, make([]byte, 4), ffo.FlatFileInformationFork.DataSize(), - ffo.FlatFileInformationFork.Platform, - ffo.FlatFileInformationFork.TypeSignature, - ffo.FlatFileInformationFork.CreatorSignature, - ffo.FlatFileInformationFork.Flags, - ffo.FlatFileInformationFork.PlatformFlags, - ffo.FlatFileInformationFork.RSVD, - ffo.FlatFileInformationFork.CreateDate, - ffo.FlatFileInformationFork.ModifyDate, - ffo.FlatFileInformationFork.NameScript, + ffo.FlatFileInformationFork.Platform[:], + ffo.FlatFileInformationFork.TypeSignature[:], + ffo.FlatFileInformationFork.CreatorSignature[:], + ffo.FlatFileInformationFork.Flags[:], + ffo.FlatFileInformationFork.PlatformFlags[:], + ffo.FlatFileInformationFork.RSVD[:], + ffo.FlatFileInformationFork.CreateDate[:], + ffo.FlatFileInformationFork.ModifyDate[:], + ffo.FlatFileInformationFork.NameScript[:], ffo.FlatFileInformationFork.ReadNameSize(), ffo.FlatFileInformationFork.Name, - ffo.FlatFileInformationFork.CommentSize, + ffo.FlatFileInformationFork.CommentSize[:], ffo.FlatFileInformationFork.Comment, ffo.FlatFileDataForkHeader.ForkType[:], ffo.FlatFileDataForkHeader.CompressionType[:], diff --git a/hotline/server_blackbox_test.go b/hotline/server_blackbox_test.go index 3e23d27..28aecf0 100644 --- a/hotline/server_blackbox_test.go +++ b/hotline/server_blackbox_test.go @@ -45,7 +45,7 @@ func tranAssertEqual(t *testing.T, tran1, tran2 []Transaction) bool { if field.ID == [2]byte{0x00, 0x72} { // FieldChatID continue } - fs = append(fs, field) + trans.Fields = append(trans.Fields, field) } trans.Fields = fs newT1 = append(newT1, trans) @@ -61,7 +61,7 @@ func tranAssertEqual(t *testing.T, tran1, tran2 []Transaction) bool { if field.ID == [2]byte{0x00, 0x72} { // FieldChatID continue } - fs = append(fs, field) + trans.Fields = append(trans.Fields, field) } trans.Fields = fs newT2 = append(newT2, trans) diff --git a/hotline/server_test.go b/hotline/server_test.go index 3a368e0..6ea8880 100644 --- a/hotline/server_test.go +++ b/hotline/server_test.go @@ -114,7 +114,7 @@ func TestServer_handleFileTransfer(t *testing.T) { Logger: NewTestLogger(), Stats: &Stats{}, fileTransfers: map[[4]byte]*FileTransfer{ - [4]byte{0, 0, 0, 5}: { + {0, 0, 0, 5}: { ReferenceNumber: []byte{0, 0, 0, 5}, Type: FileDownload, FileName: []byte("testfile-8b"), diff --git a/hotline/tracker.go b/hotline/tracker.go index 8f8ddd1..b927986 100644 --- a/hotline/tracker.go +++ b/hotline/tracker.go @@ -135,6 +135,7 @@ func GetListing(addr string) ([]ServerRecord, error) { var servers []ServerRecord for { scanner.Scan() + var srv ServerRecord _, err = srv.Write(scanner.Bytes()) if err != nil { diff --git a/hotline/transaction_handlers.go b/hotline/transaction_handlers.go index cff19d5..3d7ba14 100644 --- a/hotline/transaction_handlers.go +++ b/hotline/transaction_handlers.go @@ -373,6 +373,8 @@ func HandleSendInstantMsg(cc *ClientConn, t *Transaction) (res []Transaction, er return res, err } +var fileTypeFLDR = [4]byte{0x66, 0x6c, 0x64, 0x72} + func HandleGetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err error) { fileName := t.GetField(FieldFileName).Data filePath := t.GetField(FieldFilePath).Data @@ -396,9 +398,9 @@ func HandleGetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err e NewField(FieldFileName, []byte(encodedName)), NewField(FieldFileTypeString, fw.ffo.FlatFileInformationFork.friendlyType()), NewField(FieldFileCreatorString, fw.ffo.FlatFileInformationFork.friendlyCreator()), - NewField(FieldFileType, fw.ffo.FlatFileInformationFork.TypeSignature), - NewField(FieldFileCreateDate, fw.ffo.FlatFileInformationFork.CreateDate), - NewField(FieldFileModifyDate, fw.ffo.FlatFileInformationFork.ModifyDate), + NewField(FieldFileType, fw.ffo.FlatFileInformationFork.TypeSignature[:]), + NewField(FieldFileCreateDate, fw.ffo.FlatFileInformationFork.CreateDate[:]), + NewField(FieldFileModifyDate, fw.ffo.FlatFileInformationFork.ModifyDate[:]), } // Include the optional FileComment field if there is a comment. @@ -407,7 +409,7 @@ func HandleGetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err e } // Include the FileSize field for files. - if !bytes.Equal(fw.ffo.FlatFileInformationFork.TypeSignature, []byte{0x66, 0x6c, 0x64, 0x72}) { + if fw.ffo.FlatFileInformationFork.TypeSignature != fileTypeFLDR { fields = append(fields, NewField(FieldFileSize, fw.totalSize())) } @@ -773,7 +775,6 @@ func HandleListUsers(cc *ClientConn, t *Transaction) (res []Transaction, err err // performed. This seems to be the only place in the Hotline protocol where a data field contains another data field. func HandleUpdateUser(cc *ClientConn, t *Transaction) (res []Transaction, err error) { for _, field := range t.Fields { - var subFields []Field // Create a new scanner for parsing incoming bytes into transaction tokens @@ -1937,7 +1938,6 @@ func HandleJoinChat(cc *ClientConn, t *Transaction) (res []Transaction, err erro replyFields := []Field{NewField(FieldChatSubject, []byte(privChat.Subject))} for _, c := range sortedClients(privChat.ClientConn) { - b, err := io.ReadAll(&User{ ID: *c.ID, Icon: c.Icon, diff --git a/hotline/transfer_test.go b/hotline/transfer_test.go index 481e273..3b6c446 100644 --- a/hotline/transfer_test.go +++ b/hotline/transfer_test.go @@ -126,7 +126,7 @@ func Test_receiveFile(t *testing.T) { ForkCount: [2]byte{0, 2}, }, FlatFileInformationForkHeader: FlatFileForkHeader{}, - FlatFileInformationFork: NewFlatFileInformationFork("testfile.txt", make([]byte, 8), "TEXT", "TEXT"), + FlatFileInformationFork: NewFlatFileInformationFork("testfile.txt", [8]byte{}, "TEXT", "TEXT"), FlatFileDataForkHeader: FlatFileForkHeader{ ForkType: [4]byte{0x4d, 0x41, 0x43, 0x52}, // DATA CompressionType: [4]byte{0, 0, 0, 0},