]> git.r.bdr.sh - rbdr/mobius/blame - hotline/news.go
Merge pull request #90 from jhalter/refactor_client_agreement_flow
[rbdr/mobius] / hotline / news.go
CommitLineData
6988a057
JH
1package hotline
2
3import (
4 "bytes"
5 "crypto/rand"
6 "encoding/binary"
6988a057 7 "sort"
6988a057
JH
8)
9
10type ThreadedNews struct {
11 Categories map[string]NewsCategoryListData15 `yaml:"Categories"`
12}
13
14type NewsCategoryListData15 struct {
aebc4d36 15 Type []byte `yaml:"Type"` // Size 2 ; Bundle (2) or category (3)
72dd37f1
JH
16 Count []byte // Article or SubCategory count Size 2
17 NameSize byte
6988a057
JH
18 Name string `yaml:"Name"` //
19 Articles map[uint32]*NewsArtData `yaml:"Articles"` // Optional, if Type is Category
20 SubCats map[string]NewsCategoryListData15 `yaml:"SubCats"`
72dd37f1 21 GUID []byte // Size 16
6988a057
JH
22 AddSN []byte // Size 4
23 DeleteSN []byte // Size 4
6988a057
JH
24}
25
26func (newscat *NewsCategoryListData15) GetNewsArtListData() NewsArtListData {
27 var newsArts []NewsArtList
28 var newsArtsPayload []byte
29
30 for i, art := range newscat.Articles {
31 ID := make([]byte, 4)
32 binary.BigEndian.PutUint32(ID, i)
33
34 newArt := NewsArtList{
35 ID: ID,
36 TimeStamp: art.Date,
37 ParentID: art.ParentArt,
38 Flags: []byte{0, 0, 0, 0},
39 FlavorCount: []byte{0, 0},
40 Title: []byte(art.Title),
41 Poster: []byte(art.Poster),
42 ArticleSize: art.DataSize(),
43 }
44 newsArts = append(newsArts, newArt)
45 }
46
47 sort.Sort(byID(newsArts))
48
49 for _, v := range newsArts {
50 newsArtsPayload = append(newsArtsPayload, v.Payload()...)
51 }
52
53 nald := NewsArtListData{
54 ID: []byte{0, 0, 0, 0},
33265393 55 Count: len(newsArts),
6988a057
JH
56 Name: []byte{},
57 Description: []byte{},
58 NewsArtList: newsArtsPayload,
59 }
60
61 return nald
62}
63
72dd37f1 64// NewsArtData represents single news article
6988a057
JH
65type NewsArtData struct {
66 Title string `yaml:"Title"`
67 Poster string `yaml:"Poster"`
aebc4d36
JH
68 Date []byte `yaml:"Date"` // size 8
69 PrevArt []byte `yaml:"PrevArt"` // size 4
70 NextArt []byte `yaml:"NextArt"` // size 4
71 ParentArt []byte `yaml:"ParentArt"` // size 4
72 FirstChildArt []byte `yaml:"FirstChildArtArt"` // size 4
6988a057
JH
73 DataFlav []byte `yaml:"DataFlav"` // "text/plain"
74 Data string `yaml:"Data"`
75}
76
77func (art *NewsArtData) DataSize() []byte {
78 dataLen := make([]byte, 2)
79 binary.BigEndian.PutUint16(dataLen, uint16(len(art.Data)))
80
81 return dataLen
82}
83
84type NewsArtListData struct {
85 ID []byte `yaml:"ID"` // Size 4
86 Name []byte `yaml:"Name"`
87 Description []byte `yaml:"Description"` // not used?
88 NewsArtList []byte // List of articles Optional (if article count > 0)
33265393 89 Count int
6988a057
JH
90}
91
92func (nald *NewsArtListData) Payload() []byte {
93 count := make([]byte, 4)
33265393 94 binary.BigEndian.PutUint32(count, uint32(nald.Count))
6988a057
JH
95
96 out := append(nald.ID, count...)
97 out = append(out, []byte{uint8(len(nald.Name))}...)
98 out = append(out, nald.Name...)
99 out = append(out, []byte{uint8(len(nald.Description))}...)
100 out = append(out, nald.Description...)
101 out = append(out, nald.NewsArtList...)
102
103 return out
104}
105
72dd37f1 106// NewsArtList is a summarized version of a NewArtData record for display in list view
6988a057
JH
107type NewsArtList struct {
108 ID []byte // Size 4
109 TimeStamp []byte // Year (2 bytes), milliseconds (2 bytes) and seconds (4 bytes)
110 ParentID []byte // Size 4
111 Flags []byte // Size 4
112 FlavorCount []byte // Size 2
113 // Title size 1
114 Title []byte // string
115 // Poster size 1
116 // Poster Poster string
117 Poster []byte
118 FlavorList []NewsFlavorList
119 // Flavor list… Optional (if flavor count > 0)
120 ArticleSize []byte // Size 2
121}
122
123type byID []NewsArtList
124
125func (s byID) Len() int {
126 return len(s)
127}
128func (s byID) Swap(i, j int) {
129 s[i], s[j] = s[j], s[i]
130}
131func (s byID) Less(i, j int) bool {
132 return binary.BigEndian.Uint32(s[i].ID) < binary.BigEndian.Uint32(s[j].ID)
133}
134
135func (nal *NewsArtList) Payload() []byte {
136 out := append(nal.ID, nal.TimeStamp...)
137 out = append(out, nal.ParentID...)
138 out = append(out, nal.Flags...)
139
140 out = append(out, []byte{0, 1}...)
141
142 out = append(out, []byte{uint8(len(nal.Title))}...)
143 out = append(out, nal.Title...)
144 out = append(out, []byte{uint8(len(nal.Poster))}...)
145 out = append(out, nal.Poster...)
146 out = append(out, []byte{0x0a, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e}...) // TODO: wat?
147 out = append(out, nal.ArticleSize...)
148
149 return out
150}
151
152type NewsFlavorList struct {
153 // Flavor size 1
154 // Flavor text size MIME type string
155 // Article size 2
156}
157
72dd37f1 158func (newscat *NewsCategoryListData15) MarshalBinary() (data []byte, err error) {
6988a057
JH
159 count := make([]byte, 2)
160 binary.BigEndian.PutUint16(count, uint16(len(newscat.Articles)+len(newscat.SubCats)))
161
162 out := append(newscat.Type, count...)
163
164 if bytes.Equal(newscat.Type, []byte{0, 3}) {
72dd37f1 165 // Generate a random GUID // TODO: does this need to be random?
6988a057
JH
166 b := make([]byte, 16)
167 _, err := rand.Read(b)
168 if err != nil {
72dd37f1 169 return data, err
6988a057
JH
170 }
171
172 out = append(out, b...) // GUID
173 out = append(out, []byte{0, 0, 0, 1}...) // Add SN (TODO: not sure what this is)
174 out = append(out, []byte{0, 0, 0, 2}...) // Delete SN (TODO: not sure what this is)
175 }
176
177 out = append(out, newscat.nameLen()...)
178 out = append(out, []byte(newscat.Name)...)
179
72dd37f1 180 return out, err
6988a057
JH
181}
182
183// ReadNewsCategoryListData parses a byte slice into a NewsCategoryListData15 struct
184// For use on the client side
185func ReadNewsCategoryListData(payload []byte) NewsCategoryListData15 {
186 ncld := NewsCategoryListData15{
187 Type: payload[0:2],
188 Count: payload[2:4],
189 }
190
191 if bytes.Equal(ncld.Type, []byte{0, 3}) {
192 ncld.GUID = payload[4:20]
193 ncld.AddSN = payload[20:24]
194 ncld.AddSN = payload[24:28]
195 ncld.Name = string(payload[29:])
196 } else {
197 ncld.Name = string(payload[5:])
198 }
199
200 return ncld
201}
202
203func (newscat *NewsCategoryListData15) nameLen() []byte {
204 return []byte{uint8(len(newscat.Name))}
205}
206
8eb43f95 207// TODO: re-implement as bufio.Scanner interface
6988a057
JH
208func ReadNewsPath(newsPath []byte) []string {
209 if len(newsPath) == 0 {
210 return []string{}
211 }
212 pathCount := binary.BigEndian.Uint16(newsPath[0:2])
213
214 pathData := newsPath[2:]
215 var paths []string
216
217 for i := uint16(0); i < pathCount; i++ {
218 pathLen := pathData[2]
219 paths = append(paths, string(pathData[3:3+pathLen]))
220
221 pathData = pathData[pathLen+3:]
222 }
223
224 return paths
225}
226
227func (s *Server) GetNewsCatByPath(paths []string) map[string]NewsCategoryListData15 {
228 cats := s.ThreadedNews.Categories
229 for _, path := range paths {
230 cats = cats[path].SubCats
231 }
232 return cats
233}