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.
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?)
if err != nil {
return nil, err
}
- defer file.Close()
+ defer func(file *os.File) { _ = file.Close() }(file)
fileInfo, err := file.Stat()
if err != nil {
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},
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},
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)
})
}
--- /dev/null
+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
+}
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"
+ }(),
},
},
},
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}),
},
},
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)
}