]> git.r.bdr.sh - rbdr/mobius/commitdiff
Add partial support for file create/modify timestamps
authorJeff Halter <redacted>
Thu, 26 May 2022 00:44:55 +0000 (17:44 -0700)
committerJeff Halter <redacted>
Thu, 26 May 2022 02:51:33 +0000 (19:51 -0700)
This replaces hardcoded placeholder create/modify timestamps with the real times, mostly.  Create time is OS specific, so for now I'm ignoring it at using the modify time as a stand-in.

hotline/flattened_file_object.go
hotline/flattened_file_object_test.go
hotline/time.go [new file with mode: 0644]
hotline/transaction_handlers_test.go

index bd281e070b855726952217ac2e05eebd6221b1e2..0a14df006ff8208d71464e60f833999862114c63 100644 (file)
@@ -55,17 +55,17 @@ type FlatFileInformationFork struct {
        Comment          []byte // File comment
 }
 
-func NewFlatFileInformationFork(fileName string) FlatFileInformationFork {
+func NewFlatFileInformationFork(fileName string, modifyTime []byte) FlatFileInformationFork {
        return FlatFileInformationFork{
-               Platform:         []byte("AMAC"),                                         // TODO: Remove hardcode to support "AWIN" Platform (maybe?)
-               TypeSignature:    []byte(fileTypeFromFilename(fileName).TypeCode),        // TODO: Don't infer types from filename
-               CreatorSignature: []byte(fileTypeFromFilename(fileName).CreatorCode),     // 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:       []byte{0x07, 0x70, 0x00, 0x00, 0xba, 0x74, 0x24, 0x73}, // TODO: implement
-               ModifyDate:       []byte{0x07, 0x70, 0x00, 0x00, 0xba, 0x74, 0x24, 0x73}, // TODO: implement
-               NameScript:       make([]byte, 2),                                        // TODO: What is this?
+               Platform:         []byte("AMAC"),                                     // TODO: Remove hardcode to support "AWIN" Platform (maybe?)
+               TypeSignature:    []byte(fileTypeFromFilename(fileName).TypeCode),    // TODO: Don't infer types from filename
+               CreatorSignature: []byte(fileTypeFromFilename(fileName).CreatorCode), // 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
+               ModifyDate:       modifyTime,
+               NameScript:       make([]byte, 2), // TODO: What is this?
                Name:             []byte(fileName),
                CommentSize:      []byte{0, 4},
                Comment:          []byte("TODO"), // TODO: implement (maybe?)
@@ -204,7 +204,7 @@ func NewFlattenedFileObject(fileRoot string, filePath, fileName []byte) (*flatte
        if err != nil {
                return nil, err
        }
-       defer file.Close()
+       defer func(file *os.File) { _ = file.Close() }(file)
 
        fileInfo, err := file.Stat()
        if err != nil {
@@ -214,9 +214,11 @@ func NewFlattenedFileObject(fileRoot string, filePath, fileName []byte) (*flatte
        dataSize := make([]byte, 4)
        binary.BigEndian.PutUint32(dataSize, uint32(fileInfo.Size()))
 
+       mTime := toHotlineTime(fileInfo.ModTime())
+
        return &flattenedFileObject{
                FlatFileHeader:          NewFlatFileHeader(),
-               FlatFileInformationFork: NewFlatFileInformationFork(string(fileName)),
+               FlatFileInformationFork: NewFlatFileInformationFork(string(fileName), mTime),
                FlatFileDataForkHeader: FlatFileDataForkHeader{
                        ForkType:        []byte("DATA"),
                        CompressionType: []byte{0, 0, 0, 0},
index 5bbaf1d6fdd9e5ef0e80f96ff9b06a345eb33b92..12b6d32c260fe45696ff16b4e1dd728970f7c9d4 100644 (file)
@@ -43,7 +43,7 @@ func TestNewFlattenedFileObject(t *testing.T) {
                        want: &flattenedFileObject{
                                FlatFileHeader:                NewFlatFileHeader(),
                                FlatFileInformationForkHeader: FlatFileInformationForkHeader{},
-                               FlatFileInformationFork:       NewFlatFileInformationFork("testfile.txt"),
+                               FlatFileInformationFork:       NewFlatFileInformationFork("testfile.txt", make([]byte, 8)),
                                FlatFileDataForkHeader: FlatFileDataForkHeader{
                                        ForkType:        []byte("DATA"),
                                        CompressionType: []byte{0, 0, 0, 0},
@@ -67,9 +67,14 @@ func TestNewFlattenedFileObject(t *testing.T) {
        for _, tt := range tests {
                t.Run(tt.name, func(t *testing.T) {
                        got, err := NewFlattenedFileObject(tt.args.fileRoot, tt.args.filePath, tt.args.fileName)
-                       if !tt.wantErr(t, err, fmt.Sprintf("NewFlattenedFileObject(%v, %v, %v)", tt.args.fileRoot, tt.args.filePath, tt.args.fileName)) {
+                       if tt.wantErr(t, err, fmt.Sprintf("NewFlattenedFileObject(%v, %v, %v)", tt.args.fileRoot, tt.args.filePath, tt.args.fileName)) {
                                return
                        }
+
+                       // Clear the file timestamp fields to work around problems running the tests in multiple timezones
+                       // TODO: revisit how to test this by mocking the stat calls
+                       got.FlatFileInformationFork.CreateDate = make([]byte, 8)
+                       got.FlatFileInformationFork.ModifyDate = make([]byte, 8)
                        assert.Equalf(t, tt.want, got, "NewFlattenedFileObject(%v, %v, %v)", tt.args.fileRoot, tt.args.filePath, tt.args.fileName)
                })
        }
diff --git a/hotline/time.go b/hotline/time.go
new file mode 100644 (file)
index 0000000..3b87864
--- /dev/null
@@ -0,0 +1,25 @@
+package hotline
+
+import (
+       "encoding/binary"
+       "time"
+)
+
+// toHotlineTime converts a time.Time to the 8 byte Hotline time format:
+// Year (2 bytes), milliseconds (2 bytes) and seconds (4 bytes)
+func toHotlineTime(t time.Time) (b []byte) {
+       yearBytes := make([]byte, 2)
+       secondBytes := make([]byte, 4)
+
+       // Get a time.Time for January 1st 00:00 from t so we can calculate the difference in seconds from t
+       startOfYear := time.Date(t.Year(), time.January, 1, 0, 0, 0, 0, time.Local)
+
+       binary.BigEndian.PutUint16(yearBytes, uint16(t.Year()))
+       binary.BigEndian.PutUint32(secondBytes, uint32(t.Sub(startOfYear).Seconds()))
+
+       b = append(b, yearBytes...)
+       b = append(b, []byte{0, 0}...)
+       b = append(b, secondBytes...)
+
+       return b
+}
index f8d3b2ef65b5f8fba1a17ea350730257bb3c485b..b7340491e034524507faf46e10bdc2ccedeb1f27 100644 (file)
@@ -469,7 +469,10 @@ func TestHandleGetFileInfo(t *testing.T) {
                                        ID: &[]byte{0x00, 0x01},
                                        Server: &Server{
                                                Config: &Config{
-                                                       FileRoot: func() string { path, _ := os.Getwd(); return path + "/test/config/Files" }(),
+                                                       FileRoot: func() string {
+                                                               path, _ := os.Getwd()
+                                                               return path + "/test/config/Files"
+                                                       }(),
                                                },
                                        },
                                },
@@ -493,8 +496,8 @@ func TestHandleGetFileInfo(t *testing.T) {
                                                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(fieldFileCreateDate, make([]byte, 8)),
+                                               NewField(fieldFileModifyDate, make([]byte, 8)),
                                                NewField(fieldFileSize, []byte{0x0, 0x0, 0x0, 0x17}),
                                        },
                                },
@@ -511,6 +514,11 @@ func TestHandleGetFileInfo(t *testing.T) {
                                t.Errorf("HandleGetFileInfo() error = %v, wantErr %v", err, tt.wantErr)
                                return
                        }
+
+                       // Clear the file timestamp fields to work around problems running the tests in multiple timezones
+                       // TODO: revisit how to test this by mocking the stat calls
+                       gotRes[0].Fields[5].Data = make([]byte, 8)
+                       gotRes[0].Fields[6].Data = make([]byte, 8)
                        if !assert.Equal(t, tt.wantRes, gotRes) {
                                t.Errorf("HandleGetFileInfo() gotRes = %v, want %v", gotRes, tt.wantRes)
                        }