state.go raw
1 package main
2
3 import (
4 "common/helpers"
5 "common/jsbridge/sw"
6 )
7
8 // Shared state not yet assigned to a specific domain.
9 var (
10 cryptoCBs map[int]func(string, string)
11 nextCryptoID int
12 )
13
14 func initSharedState() {
15 cryptoCBs = make(map[int]func(string, string))
16 }
17
18 // cryptoProxy sends a crypto request to the page's signer extension
19 // and calls cb with (result, error) when CRYPTO_RESULT comes back.
20 func cryptoProxy(method, peerPubkey, data string, cb func(string, string)) {
21 id := nextCryptoID
22 nextCryptoID++
23 cryptoCBs[id] = cb
24 sw.Log("shell: crypto→page " + method + " #" + helpers.Itoa(int64(id)))
25 broadcastToClients("[\"CRYPTO_REQ\"," + helpers.Itoa(int64(id)) + "," + jstr(method) + "," + jstr(peerPubkey) + "," + jstr(data) + "]")
26 capturedID := id
27 sw.SetTimeout(15000, func() {
28 if fn, ok := cryptoCBs[capturedID]; ok {
29 delete(cryptoCBs, capturedID)
30 sw.Log("shell: crypto TIMEOUT #" + helpers.Itoa(int64(capturedID)))
31 fn("", "crypto proxy timeout")
32 }
33 })
34 }
35
36 // --- Shared utilities ---
37
38 func sendToClient(clientID, msg string) {
39 if clientID == "" {
40 broadcastToClients(msg)
41 return
42 }
43 sw.GetClientByID(clientID, func(c sw.Client, ok bool) {
44 if ok {
45 sw.PostMessageJSON(c, msg)
46 } else {
47 // Client gone (tab closed, reloaded) — broadcast to all windows.
48 sw.Log("shell: client gone " + clientID[:8] + "… → broadcast")
49 broadcastToClients(msg)
50 }
51 })
52 }
53
54 func broadcastToClients(msg string) {
55 sw.MatchClients(func(c sw.Client) {
56 sw.PostMessageJSON(c, msg)
57 })
58 }
59
60 func jstr(s string) string { return helpers.JsonString(s) }
61
62 // mw is a JSON array message walker for parsing client messages.
63 type mw struct {
64 s string
65 i int
66 }
67
68 func newMW(s string) mw {
69 w := mw{s: s}
70 for w.i < len(s) && s[w.i] != '[' {
71 w.i++
72 }
73 if w.i < len(s) {
74 w.i++
75 }
76 return w
77 }
78
79 func (w *mw) sep() {
80 for w.i < len(w.s) {
81 c := w.s[w.i]
82 if c != ' ' && c != '\n' && c != '\r' && c != '\t' && c != ',' {
83 break
84 }
85 w.i++
86 }
87 }
88
89 func (w *mw) str() string {
90 w.sep()
91 if w.i >= len(w.s) || w.s[w.i] != '"' {
92 return ""
93 }
94 w.i++
95 var buf []byte
96 hasEsc := false
97 start := w.i
98 for w.i < len(w.s) && w.s[w.i] != '"' {
99 if w.s[w.i] == '\\' && w.i+1 < len(w.s) {
100 hasEsc = true
101 buf = append(buf, w.s[start:w.i]...)
102 w.i++
103 switch w.s[w.i] {
104 case '"', '\\', '/':
105 buf = append(buf, w.s[w.i])
106 case 'n':
107 buf = append(buf, '\n')
108 case 't':
109 buf = append(buf, '\t')
110 case 'r':
111 buf = append(buf, '\r')
112 case 'u':
113 if w.i+4 < len(w.s) {
114 var cp int
115 for k := 1; k <= 4; k++ {
116 c := w.s[w.i+k]
117 cp <<= 4
118 if c >= '0' && c <= '9' {
119 cp |= int(c - '0')
120 } else if c >= 'a' && c <= 'f' {
121 cp |= int(c-'a') + 10
122 } else if c >= 'A' && c <= 'F' {
123 cp |= int(c-'A') + 10
124 }
125 }
126 if cp < 0x80 {
127 buf = append(buf, byte(cp))
128 } else if cp < 0x800 {
129 buf = append(buf, byte(0xC0|(cp>>6)), byte(0x80|(cp&0x3F)))
130 } else {
131 buf = append(buf, byte(0xE0|(cp>>12)), byte(0x80|((cp>>6)&0x3F)), byte(0x80|(cp&0x3F)))
132 }
133 w.i += 4
134 } else {
135 buf = append(buf, '\\', 'u')
136 }
137 default:
138 buf = append(buf, '\\', w.s[w.i])
139 }
140 w.i++
141 start = w.i
142 continue
143 }
144 w.i++
145 }
146 if w.i >= len(w.s) {
147 return ""
148 }
149 var result string
150 if hasEsc {
151 buf = append(buf, w.s[start:w.i]...)
152 result = string(buf)
153 } else {
154 result = w.s[start:w.i]
155 }
156 w.i++
157 return result
158 }
159
160 func (w *mw) num() int64 {
161 w.sep()
162 neg := false
163 if w.i < len(w.s) && w.s[w.i] == '-' {
164 neg = true
165 w.i++
166 }
167 var n int64
168 for w.i < len(w.s) && w.s[w.i] >= '0' && w.s[w.i] <= '9' {
169 n = n*10 + int64(w.s[w.i]-'0')
170 w.i++
171 }
172 if neg {
173 n = -n
174 }
175 return n
176 }
177
178 func (w *mw) raw() string {
179 w.sep()
180 start := w.i
181 w.i = skipval(w.s, w.i)
182 if w.i < 0 {
183 w.i = len(w.s)
184 return ""
185 }
186 return w.s[start:w.i]
187 }
188
189 func (w *mw) strs() []string {
190 w.sep()
191 if w.i >= len(w.s) || w.s[w.i] != '[' {
192 return nil
193 }
194 w.i++
195 var out []string
196 for {
197 w.sep()
198 if w.i >= len(w.s) {
199 return out
200 }
201 if w.s[w.i] == ']' {
202 w.i++
203 return out
204 }
205 if w.s[w.i] != '"' {
206 return out
207 }
208 out = append(out, w.str())
209 }
210 }
211
212 func skipval(s string, i int) int {
213 if i >= len(s) {
214 return -1
215 }
216 switch s[i] {
217 case '"':
218 i++
219 for i < len(s) {
220 if s[i] == '\\' {
221 i += 2
222 continue
223 }
224 if s[i] == '"' {
225 return i + 1
226 }
227 i++
228 }
229 return -1
230 case '{':
231 return skipBrack(s, i, '{', '}')
232 case '[':
233 return skipBrack(s, i, '[', ']')
234 case 't':
235 if i+4 <= len(s) {
236 return i + 4
237 }
238 return -1
239 case 'f':
240 if i+5 <= len(s) {
241 return i + 5
242 }
243 return -1
244 case 'n':
245 if i+4 <= len(s) {
246 return i + 4
247 }
248 return -1
249 default:
250 for i < len(s) && s[i] != ',' && s[i] != '}' && s[i] != ']' && s[i] != ' ' && s[i] != '\n' {
251 i++
252 }
253 return i
254 }
255 }
256
257 func skipBrack(s string, i int, open, close byte) int {
258 depth := 1
259 i++
260 inStr := false
261 for i < len(s) && depth > 0 {
262 if inStr {
263 if s[i] == '\\' {
264 i++
265 } else if s[i] == '"' {
266 inStr = false
267 }
268 } else {
269 switch s[i] {
270 case '"':
271 inStr = true
272 case open:
273 depth++
274 case close:
275 depth--
276 }
277 }
278 i++
279 }
280 if depth != 0 {
281 return -1
282 }
283 return i
284 }
285