]> git.r.bdr.sh - rbdr/mobius/blame - hotline/flattened_file_object.go
Add client connection rate limit
[rbdr/mobius] / hotline / flattened_file_object.go
CommitLineData
6988a057
JH
1package hotline
2
3import (
9cf66aea 4 "bytes"
6988a057 5 "encoding/binary"
7cd900d6 6 "io"
9cf66aea 7 "slices"
6988a057
JH
8)
9
10type flattenedFileObject struct {
11 FlatFileHeader FlatFileHeader
7cd900d6 12 FlatFileInformationForkHeader FlatFileForkHeader
6988a057 13 FlatFileInformationFork FlatFileInformationFork
7cd900d6
JH
14 FlatFileDataForkHeader FlatFileForkHeader
15 FlatFileResForkHeader FlatFileForkHeader
45ca5d60
JH
16
17 readOffset int // Internal offset to track read progress
6988a057
JH
18}
19
20// FlatFileHeader is the first section of a "Flattened File Object". All fields have static values.
21type FlatFileHeader struct {
72dd37f1
JH
22 Format [4]byte // Always "FILP"
23 Version [2]byte // Always 1
24 RSVD [16]byte // Always empty zeros
7cd900d6 25 ForkCount [2]byte // Number of forks, either 2 or 3 if there is a resource fork
6988a057
JH
26}
27
28type FlatFileInformationFork struct {
a55350da
JH
29 Platform [4]byte // Operating System used. ("AMAC" or "MWIN")
30 TypeSignature [4]byte // File type signature
31 CreatorSignature [4]byte // File creator signature
32 Flags [4]byte
33 PlatformFlags [4]byte
34 RSVD [32]byte
35 CreateDate [8]byte
36 ModifyDate [8]byte
37 NameScript [2]byte
38 NameSize [2]byte // Length of file name (Maximum 128 characters)
39 Name []byte // File name
40 CommentSize [2]byte // Length of the comment
41 Comment []byte // File comment
45ca5d60
JH
42
43 readOffset int // Internal offset to track read progress
6988a057
JH
44}
45
a55350da 46func NewFlatFileInformationFork(fileName string, modifyTime [8]byte, typeSignature string, creatorSignature string) FlatFileInformationFork {
6988a057 47 return FlatFileInformationFork{
a55350da
JH
48 Platform: [4]byte{0x41, 0x4D, 0x41, 0x43}, // "AMAC" TODO: Remove hardcode to support "AWIN" Platform (maybe?)
49 TypeSignature: [4]byte([]byte(typeSignature)), // TODO: Don't infer types from filename
50 CreatorSignature: [4]byte([]byte(creatorSignature)), // TODO: Don't infer types from filename
51 PlatformFlags: [4]byte{0, 0, 1, 0}, // TODO: What is this?
52 CreateDate: modifyTime, // some filesystems don't support createTime
29f329ae 53 ModifyDate: modifyTime,
6988a057 54 Name: []byte(fileName),
5218c782 55 Comment: []byte{}, // TODO: implement (maybe?)
6988a057
JH
56 }
57}
58
fd740bc4 59func (ffif *FlatFileInformationFork) FriendlyType() []byte {
a55350da 60 if name, ok := friendlyCreatorNames[string(ffif.TypeSignature[:])]; ok {
2d52424e
JH
61 return []byte(name)
62 }
a55350da 63 return ffif.TypeSignature[:]
7cd900d6
JH
64}
65
fd740bc4 66func (ffif *FlatFileInformationFork) FriendlyCreator() []byte {
a55350da 67 if name, ok := friendlyCreatorNames[string(ffif.CreatorSignature[:])]; ok {
7cd900d6
JH
68 return []byte(name)
69 }
a55350da 70 return ffif.CreatorSignature[:]
2d52424e
JH
71}
72
fd740bc4 73func (ffif *FlatFileInformationFork) SetComment(comment []byte) error {
a55350da 74 commentSize := make([]byte, 2)
7cd900d6 75 ffif.Comment = comment
a55350da
JH
76 binary.BigEndian.PutUint16(commentSize, uint16(len(comment)))
77 ffif.CommentSize = [2]byte(commentSize)
7cd900d6
JH
78 // TODO: return err if comment is too long
79 return nil
80}
81
bb7fe19f
JH
82// DataSize calculates the size of the flat file information fork, which is
83// 72 bytes for the fixed length fields plus the length of the Name + Comment
85767504 84func (ffif *FlatFileInformationFork) DataSize() []byte {
6988a057 85 size := make([]byte, 4)
bb7fe19f 86
7cd900d6 87 dataSize := len(ffif.Name) + len(ffif.Comment) + 74 // 74 = len of fixed size headers
6988a057
JH
88
89 binary.BigEndian.PutUint32(size, uint32(dataSize))
90
91 return size
92}
93
7cd900d6
JH
94func (ffif *FlatFileInformationFork) Size() [4]byte {
95 size := [4]byte{}
96
97 dataSize := len(ffif.Name) + len(ffif.Comment) + 74 // 74 = len of fixed size headers
98
99 binary.BigEndian.PutUint32(size[:], uint32(dataSize))
100
101 return size
102}
103
104func (ffo *flattenedFileObject) TransferSize(offset int64) []byte {
a2ef262a
JH
105 ffoCopy := *ffo
106
7cd900d6 107 // get length of the flattenedFileObject, including the info fork
a2ef262a 108 b, _ := io.ReadAll(&ffoCopy)
9cf66aea 109 payloadSize := len(b)
7cd900d6
JH
110
111 // length of data fork
85767504 112 dataSize := binary.BigEndian.Uint32(ffo.FlatFileDataForkHeader.DataSize[:])
6988a057 113
7cd900d6
JH
114 // length of resource fork
115 resForkSize := binary.BigEndian.Uint32(ffo.FlatFileResForkHeader.DataSize[:])
116
117 size := make([]byte, 4)
aeb97482 118 binary.BigEndian.PutUint32(size, dataSize+resForkSize+uint32(payloadSize)-uint32(offset))
6988a057 119
7cd900d6 120 return size
6988a057
JH
121}
122
85767504 123func (ffif *FlatFileInformationFork) ReadNameSize() []byte {
6988a057
JH
124 size := make([]byte, 2)
125 binary.BigEndian.PutUint16(size, uint16(len(ffif.Name)))
126
127 return size
128}
129
7cd900d6
JH
130type FlatFileForkHeader struct {
131 ForkType [4]byte // Either INFO, DATA or MACR
85767504
JH
132 CompressionType [4]byte
133 RSVD [4]byte
134 DataSize [4]byte
6988a057
JH
135}
136
9cf66aea 137func (ffif *FlatFileInformationFork) Read(p []byte) (int, error) {
45ca5d60 138 buf := slices.Concat(
a55350da
JH
139 ffif.Platform[:],
140 ffif.TypeSignature[:],
141 ffif.CreatorSignature[:],
142 ffif.Flags[:],
143 ffif.PlatformFlags[:],
144 ffif.RSVD[:],
145 ffif.CreateDate[:],
146 ffif.ModifyDate[:],
147 ffif.NameScript[:],
45ca5d60
JH
148 ffif.ReadNameSize(),
149 ffif.Name,
a55350da 150 ffif.CommentSize[:],
45ca5d60
JH
151 ffif.Comment,
152 )
153
154 if ffif.readOffset >= len(buf) {
155 return 0, io.EOF // All bytes have been read
156 }
157
158 n := copy(p, buf[ffif.readOffset:])
159 ffif.readOffset += n
160
161 return n, nil
9cf66aea
JH
162}
163
95159e55 164// Write implements the io.Writer interface for FlatFileInformationFork
9cf66aea
JH
165func (ffif *FlatFileInformationFork) Write(p []byte) (int, error) {
166 nameSize := p[70:72]
167 bs := binary.BigEndian.Uint16(nameSize)
168 total := 72 + bs
169
a55350da
JH
170 ffif.Platform = [4]byte(p[0:4])
171 ffif.TypeSignature = [4]byte(p[4:8])
172 ffif.CreatorSignature = [4]byte(p[8:12])
173 ffif.Flags = [4]byte(p[12:16])
174 ffif.PlatformFlags = [4]byte(p[16:20])
175 ffif.RSVD = [32]byte(p[20:52])
176 ffif.CreateDate = [8]byte(p[52:60])
177 ffif.ModifyDate = [8]byte(p[60:68])
178 ffif.NameScript = [2]byte(p[68:70])
179 ffif.NameSize = [2]byte(p[70:72])
9cf66aea
JH
180 ffif.Name = p[72:total]
181
182 if len(p) > int(total) {
a55350da
JH
183 ffif.CommentSize = [2]byte(p[total : total+2])
184 commentLen := binary.BigEndian.Uint16(ffif.CommentSize[:])
9cf66aea
JH
185 commentStartPos := int(total) + 2
186 commentEndPos := int(total) + 2 + int(commentLen)
187
188 ffif.Comment = p[commentStartPos:commentEndPos]
7cd900d6 189
f8e4cd54 190 //total = uint16(commentEndPos)
9cf66aea
JH
191 }
192
45ca5d60 193 return len(p), nil
7cd900d6
JH
194}
195
85767504 196func (ffif *FlatFileInformationFork) UnmarshalBinary(b []byte) error {
85767504 197 nameSize := b[70:72]
6988a057 198 bs := binary.BigEndian.Uint16(nameSize)
85767504 199 nameEnd := 72 + bs
6988a057 200
a55350da
JH
201 ffif.Platform = [4]byte(b[0:4])
202 ffif.TypeSignature = [4]byte(b[4:8])
203 ffif.CreatorSignature = [4]byte(b[8:12])
204 ffif.Flags = [4]byte(b[12:16])
205 ffif.PlatformFlags = [4]byte(b[16:20])
206 ffif.RSVD = [32]byte(b[20:52])
207 ffif.CreateDate = [8]byte(b[52:60])
208 ffif.ModifyDate = [8]byte(b[60:68])
209 ffif.NameScript = [2]byte(b[68:70])
210 ffif.NameSize = [2]byte(b[70:72])
85767504 211 ffif.Name = b[72:nameEnd]
050407a3
JH
212
213 if len(b) > int(nameEnd) {
a55350da
JH
214 ffif.CommentSize = [2]byte(b[nameEnd : nameEnd+2])
215 commentLen := binary.BigEndian.Uint16(ffif.CommentSize[:])
050407a3
JH
216
217 commentStartPos := int(nameEnd) + 2
218 commentEndPos := int(nameEnd) + 2 + int(commentLen)
219
220 ffif.Comment = b[commentStartPos:commentEndPos]
221 }
85767504
JH
222
223 return nil
6988a057
JH
224}
225
9cf66aea
JH
226// Read implements the io.Reader interface for flattenedFileObject
227func (ffo *flattenedFileObject) Read(p []byte) (int, error) {
45ca5d60 228 buf := slices.Concat(
9cf66aea
JH
229 ffo.FlatFileHeader.Format[:],
230 ffo.FlatFileHeader.Version[:],
231 ffo.FlatFileHeader.RSVD[:],
232 ffo.FlatFileHeader.ForkCount[:],
233 []byte("INFO"),
234 []byte{0, 0, 0, 0},
235 make([]byte, 4),
236 ffo.FlatFileInformationFork.DataSize(),
a55350da
JH
237 ffo.FlatFileInformationFork.Platform[:],
238 ffo.FlatFileInformationFork.TypeSignature[:],
239 ffo.FlatFileInformationFork.CreatorSignature[:],
240 ffo.FlatFileInformationFork.Flags[:],
241 ffo.FlatFileInformationFork.PlatformFlags[:],
242 ffo.FlatFileInformationFork.RSVD[:],
243 ffo.FlatFileInformationFork.CreateDate[:],
244 ffo.FlatFileInformationFork.ModifyDate[:],
245 ffo.FlatFileInformationFork.NameScript[:],
9cf66aea
JH
246 ffo.FlatFileInformationFork.ReadNameSize(),
247 ffo.FlatFileInformationFork.Name,
a55350da 248 ffo.FlatFileInformationFork.CommentSize[:],
9cf66aea
JH
249 ffo.FlatFileInformationFork.Comment,
250 ffo.FlatFileDataForkHeader.ForkType[:],
251 ffo.FlatFileDataForkHeader.CompressionType[:],
252 ffo.FlatFileDataForkHeader.RSVD[:],
253 ffo.FlatFileDataForkHeader.DataSize[:],
45ca5d60
JH
254 )
255
256 if ffo.readOffset >= len(buf) {
257 return 0, io.EOF // All bytes have been read
258 }
259
260 n := copy(p, buf[ffo.readOffset:])
261 ffo.readOffset += n
262
263 return n, nil
6988a057
JH
264}
265
9cf66aea
JH
266func (ffo *flattenedFileObject) ReadFrom(r io.Reader) (int64, error) {
267 var n int64
7cd900d6
JH
268
269 if err := binary.Read(r, binary.BigEndian, &ffo.FlatFileHeader); err != nil {
270 return n, err
92a7e455 271 }
7cd900d6
JH
272
273 if err := binary.Read(r, binary.BigEndian, &ffo.FlatFileInformationForkHeader); err != nil {
274 return n, err
6988a057 275 }
16a4ad70 276
7cd900d6
JH
277 dataLen := binary.BigEndian.Uint32(ffo.FlatFileInformationForkHeader.DataSize[:])
278 ffifBuf := make([]byte, dataLen)
279 if _, err := io.ReadFull(r, ffifBuf); err != nil {
280 return n, err
281 }
6988a057 282
9cf66aea
JH
283 _, err := io.Copy(&ffo.FlatFileInformationFork, bytes.NewReader(ffifBuf))
284 if err != nil {
7cd900d6 285 return n, err
6988a057
JH
286 }
287
7cd900d6
JH
288 if err := binary.Read(r, binary.BigEndian, &ffo.FlatFileDataForkHeader); err != nil {
289 return n, err
290 }
6988a057 291
7cd900d6
JH
292 return n, nil
293}
29f329ae 294
7cd900d6
JH
295func (ffo *flattenedFileObject) dataSize() int64 {
296 return int64(binary.BigEndian.Uint32(ffo.FlatFileDataForkHeader.DataSize[:]))
297}
2d52424e 298
7cd900d6
JH
299func (ffo *flattenedFileObject) rsrcSize() int64 {
300 return int64(binary.BigEndian.Uint32(ffo.FlatFileResForkHeader.DataSize[:]))
6988a057 301}