]> git.r.bdr.sh - rbdr/mobius/blob - hotline/field.go
Replace hardcoded version with ldflag usage
[rbdr/mobius] / hotline / field.go
1 package hotline
2
3 import (
4 "encoding/binary"
5 "io"
6 "slices"
7 )
8
9 // List of Hotline protocol field types taken from the official 1.9 protocol document
10 const (
11 FieldError = 100
12 FieldData = 101
13 FieldUserName = 102
14 FieldUserID = 103
15 FieldUserIconID = 104
16 FieldUserLogin = 105
17 FieldUserPassword = 106
18 FieldRefNum = 107
19 FieldTransferSize = 108
20 FieldChatOptions = 109
21 FieldUserAccess = 110
22 FieldUserAlias = 111 // TODO: implement
23 FieldUserFlags = 112
24 FieldOptions = 113
25 FieldChatID = 114
26 FieldChatSubject = 115
27 FieldWaitingCount = 116
28 FieldBannerType = 152
29 FieldNoServerAgreement = 152
30 FieldVersion = 160
31 FieldCommunityBannerID = 161
32 FieldServerName = 162
33 FieldFileNameWithInfo = 200
34 FieldFileName = 201
35 FieldFilePath = 202
36 FieldFileResumeData = 203
37 FieldFileTransferOptions = 204
38 FieldFileTypeString = 205
39 FieldFileCreatorString = 206
40 FieldFileSize = 207
41 FieldFileCreateDate = 208
42 FieldFileModifyDate = 209
43 FieldFileComment = 210
44 FieldFileNewName = 211
45 FieldFileNewPath = 212
46 FieldFileType = 213
47 FieldQuotingMsg = 214
48 FieldAutomaticResponse = 215
49 FieldFolderItemCount = 220
50 FieldUsernameWithInfo = 300
51 FieldNewsArtListData = 321
52 FieldNewsCatName = 322
53 FieldNewsCatListData15 = 323
54 FieldNewsPath = 325
55 FieldNewsArtID = 326
56 FieldNewsArtDataFlav = 327
57 FieldNewsArtTitle = 328
58 FieldNewsArtPoster = 329
59 FieldNewsArtDate = 330
60 FieldNewsArtPrevArt = 331
61 FieldNewsArtNextArt = 332
62 FieldNewsArtData = 333
63 FieldNewsArtFlags = 334 // TODO: what is this used for?
64 FieldNewsArtParentArt = 335
65 FieldNewsArt1stChildArt = 336
66 FieldNewsArtRecurseDel = 337 // TODO: implement news article recusive deletion
67 )
68
69 type Field struct {
70 ID [2]byte // Type of field
71 FieldSize [2]byte // Size of the data part
72 Data []byte // Actual field content
73
74 readOffset int // Internal offset to track read progress
75 }
76
77 type requiredField struct {
78 ID int
79 minLen int
80 }
81
82 func NewField(id uint16, data []byte) Field {
83 idBytes := make([]byte, 2)
84 binary.BigEndian.PutUint16(idBytes, id)
85
86 bs := make([]byte, 2)
87 binary.BigEndian.PutUint16(bs, uint16(len(data)))
88
89 return Field{
90 ID: [2]byte(idBytes),
91 FieldSize: [2]byte(bs),
92 Data: data,
93 }
94 }
95
96 // fieldScanner implements bufio.SplitFunc for parsing byte slices into complete tokens
97 func fieldScanner(data []byte, _ bool) (advance int, token []byte, err error) {
98 if len(data) < minFieldLen {
99 return 0, nil, nil
100 }
101
102 // tranLen represents the length of bytes that are part of the transaction
103 neededSize := minFieldLen + int(binary.BigEndian.Uint16(data[2:4]))
104 if neededSize > len(data) {
105 return 0, nil, nil
106 }
107
108 return neededSize, data[0:neededSize], nil
109 }
110
111 // Read implements io.Reader for Field
112 func (f *Field) Read(p []byte) (int, error) {
113 buf := slices.Concat(f.ID[:], f.FieldSize[:], f.Data)
114
115 if f.readOffset >= len(buf) {
116 return 0, io.EOF // All bytes have been read
117 }
118
119 n := copy(p, buf[f.readOffset:])
120 f.readOffset += n
121
122 return n, nil
123 }
124
125 // Write implements io.Writer for Field
126 func (f *Field) Write(p []byte) (int, error) {
127 f.ID = [2]byte(p[0:2])
128 f.FieldSize = [2]byte(p[2:4])
129
130 i := int(binary.BigEndian.Uint16(f.FieldSize[:]))
131 f.Data = p[4 : 4+i]
132
133 return minFieldLen + i, nil
134 }
135
136 func getField(id int, fields *[]Field) *Field {
137 for _, field := range *fields {
138 if id == int(binary.BigEndian.Uint16(field.ID[:])) {
139 return &field
140 }
141 }
142 return nil
143 }