nx_torrent_daemon.nx source
↩ module page · 1321 lines · 79296 B
1// nx_torrent_daemon.nx -- the UI enabler (X-TORRENT-UI-001): an HTTP daemon that
2// serves the operator's torrent page and wires it to the LIVE download worker.
3//
4// GET /torrent -> sites/nishifamily/torrent/index.html
5// GET /torrent/app.js -> the page's last-mile JS (renders REAL state only)
6// GET /torrent/api/me -> {"user":"local"} (local single-operator)
7// POST /torrent/api/login -> {"ok":true}
8// POST /torrent/api/add {magnet} -> fork+execve the worker elf -> {"id":"1"}
9// GET /torrent/api/status -> reads outdir/download.status -> {downloads:[...]}
10// POST /torrent/api/<id>/<action> -> {"ok":true} (pause/resume/remove noop)
11//
12// Sovereign: composes nx_http_server (bind/listen/accept) + the worker elf the team
13// already built. NO mocked numbers -- /status emits exactly what the worker wrote to
14// disk; before any download the worker file is absent -> {downloads:[]} (honest empty).
15// license_tier: ORIGINAL kind: last-mile-ui-bridge layer: L6 (over L4 http + L5 worker)
16//
17// module: nishi-core.torrent.daemon
18// depends: nishi-core.http_server, nishi-core.syscalls
19import "nx_http_server.nx"
20import "nx_syscalls.nx"
21import "nx_itoa_lib.nx" // shared MSB-first emitter: nxi_buf is the NUL-free buffer form
22import "nx_bencode.nx" // parse download.meta to locate the video file's byte-range for streaming
23import "nx_torrent_access.nx" // R2: deny-by-default AREA policy (SFW=media family+, NSFW=gallery owner-only)
24const TORRENT_MAGIC_131072: i64 = 131072
25const TORRENT_MAGIC_1024: i64 = 1024
26const TORRENT_MAGIC_8192: i64 = 8192
27const TORRENT_MAGIC_262144: i64 = 262144
28const TORRENT_MAGIC_4000: i64 = 4000
29const TORRENT_MAGIC_65536: i64 = 65536
30const TORRENT_MAGIC_2000: i64 = 2000
31const TORRENT_MAGIC_1048576: i64 = 1048576
32const TORRENT_MAGIC_4096: i64 = 4096
33const TORRENT_MAGIC_4095: i64 = 4095
34const TORRENT_MAGIC_16384: i64 = 16384
35const TORRENT_MAGIC_4200: i64 = 4200
36const TORRENT_MAGIC_8097: i64 = 8097
37const TORRENT_MAGIC_1000000000: i64 = 1000000000
38
39// R3 auth: the OPAQUE session->level resolution lives in the FRONT GATEWAY (runtime/_hdl_build/nx_torrent_authn,
40// proven by nx_torrent_authn_gate 14/14) -- NOT in this daemon, which stays LEAN (the full OPAQUE stack won't
41// co-compile here -> empty .s). The gateway authenticates, resolves the level, and injects it as a TRUSTED
42// internal header X-Nishi-Level (Cardinal 12: validate at the boundary, trust internal). LOCAL marker = the
43// operator's own box (a hub push never carries this workstation-local file) -> OWNER.
44const TORRENT_LOCAL_MARKER: *u8 = "/mnt/c/Users/elder/Downloads/nishi-torrents/.local_operator"
45
46// PERSISTENT RETRY (rung #2): how often the detached sweeper re-spawns workers for STUCK partials (a
47// worker gives up after ~20min of no new pieces, then the torrent sits idle). 10 min = tracker-friendly
48// re-announce cadence that still grabs a seed soon after it appears. Data-driven (Cardinal 11).
49const D_RETRY_INTERVAL_MS: i64 = 600000
50
51// ---- small buffer-building helpers -------------------------------
52func d_strlen(s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){i=i+1} return i }
53
54func d_puts(dst: *u8, off: i64, s: *u8) -> i64 {
55 var i: i64 = 0
56 while s[i] != (0 as u8) { dst[off+i] = s[i]; i = i + 1 }
57 return off + i
58}
59// MIGRATED to nxi_buf (debt 1785557603). The old body mmapped a reverse buffer per call and never
60// freed it -- 21 call sites in a LONG-LIVED daemon, so the leak was per-call. It also carried the
61// documented clone SIGN BUG: it did m = 0 - m for negatives but NEVER WROTE THE MINUS, so every
62// negative printed as positive. nxi_buf is MSB-first, allocates nothing, emits the sign (gate T9/T10)
63// and keeps this exact contract: digits only, NO NUL, returns off+len.
64func d_putn(dst: *u8, off: i64, v: i64) -> i64 { return nxi_buf(dst, off, v) }
65
66// read a whole file into buf (bounded by cap); returns bytes read or -1 if open fails
67func d_read_file(path: *u8, buf: *u8, cap: i64) -> i64 {
68 let fd: i64 = sys_openat_rd(path)
69 if fd < 0 { return 0 - 1 }
70 var off: i64 = 0; var go: i64 = 1
71 while go == 1 {
72 if off >= cap { go = 0 } else {
73 let r: i64 = sys_read(fd, buf + off, cap - off)
74 if r <= 0 { go = 0 } else { off = off + r }
75 }
76 }
77 sys_close(fd)
78 return off
79}
80
81// build outdir+suffix into out (NUL-terminated); returns out. (status/control/magnet sidecars)
82func d_path(outdir: *u8, suf: *u8, out: *u8) -> *u8 {
83 var o: i64 = d_puts(out, 0, outdir)
84 o = d_puts(out, o, suf)
85 out[o] = 0 as u8
86 return out
87}
88// write n bytes to path (O_CREAT|O_WRONLY|O_TRUNC) -- the control + magnet sidecar files
89func d_write_file(path: *u8, s: *u8, n: i64) -> i64 {
90 let fd: i64 = sys_openat_wr(path, 0x1a4)
91 if fd < 0 { return 0 - 1 }
92 sys_write(fd, s, n)
93 sys_close(fd)
94 return 0
95}
96
97// ---- HTTP response writer (header + body, then close) ------------
98func d_send(cfd: i64, status: *u8, ctype: *u8, body: *u8, blen: i64) -> i64 {
99 let h: *u8 = sys_mmap(512)
100 var o: i64 = 0
101 o = d_puts(h, o, "HTTP/1.1 " as *u8); o = d_puts(h, o, status)
102 o = d_puts(h, o, "\r\nContent-Type: " as *u8); o = d_puts(h, o, ctype)
103 o = d_puts(h, o, "\r\nContent-Length: " as *u8); o = d_putn(h, o, blen)
104 o = d_puts(h, o, "\r\nAccess-Control-Allow-Origin: *\r\nCache-Control: no-store\r\nConnection: close\r\n\r\n" as *u8)
105 sys_write(cfd, h, o)
106 if blen > 0 { sys_write(cfd, body, blen) }
107 sys_close(cfd)
108 return 0
109}
110func d_send_str(cfd: i64, status: *u8, ctype: *u8, s: *u8) -> i64 {
111 return d_send(cfd, status, ctype, s, d_strlen(s))
112}
113func d_serve_file(cfd: i64, path: *u8, ctype: *u8) -> i64 {
114 let buf: *u8 = sys_mmap(TORRENT_MAGIC_131072)
115 let n: i64 = d_read_file(path, buf, TORRENT_MAGIC_131072)
116 if n <= 0 { d_send_str(cfd, "404 Not Found" as *u8, "text/plain" as *u8, "missing\n" as *u8); return 0 }
117 d_send(cfd, "200 OK" as *u8, ctype, buf, n)
118 return 0
119}
120
121// ---- path matching on the parsed request -------------------------
122func d_streq(buf: *u8, off: i64, len: i64, s: *u8) -> i64 {
123 var i: i64 = 0
124 while s[i] != (0 as u8) {
125 if i >= len { return 0 }
126 if buf[off+i] != s[i] { return 0 }
127 i = i + 1
128 }
129 if i != len { return 0 }
130 return 1
131}
132func d_starts(buf: *u8, off: i64, len: i64, s: *u8) -> i64 {
133 var i: i64 = 0
134 while s[i] != (0 as u8) {
135 if i >= len { return 0 }
136 if buf[off+i] != s[i] { return 0 }
137 i = i + 1
138 }
139 return 1
140}
141
142// ---- magnet extraction: copy the "magnet:..." value out of the JSON body ----
143// finds the byte run m a g n e t : (the value; the key "magnet" is followed by a quote)
144// and copies to the next double-quote. Returns length.
145func d_extract_magnet(body: *u8, blen: i64, out: *u8, ocap: i64) -> i64 {
146 var i: i64 = 0; var start: i64 = 0 - 1
147 while i + 7 <= blen {
148 if body[i]==(109 as u8) { if body[i+1]==(97 as u8) { if body[i+2]==(103 as u8) { if body[i+3]==(110 as u8) { if body[i+4]==(101 as u8) { if body[i+5]==(116 as u8) { if body[i+6]==(58 as u8) {
149 start = i; i = blen + 100
150 }}}}}}}
151 i = i + 1
152 }
153 if start < 0 { return 0 }
154 var j: i64 = start; var k: i64 = 0; var go: i64 = 1
155 while go == 1 {
156 if j >= blen { go = 0 } else {
157 if body[j]==(34 as u8) { go = 0 } else {
158 if k < ocap - 1 { out[k] = body[j]; k = k + 1 }
159 j = j + 1
160 }
161 }
162 }
163 out[k] = 0 as u8
164 return k
165}
166
167// ---- spawn the worker elf (double-fork so it reparents to init: no zombie) ----
168// has this torrent already made progress? (non-empty donefile) -> re-add should RESUME, not truncate.
169func d_has_progress(outdir: *u8) -> i64 {
170 let dp: *u8 = sys_mmap(640); var p: i64 = d_puts(dp, 0, outdir); p = d_puts(dp, p, "/download.done" as *u8); dp[p] = 0 as u8
171 let fd: i64 = sys_openat_rd(dp); if fd < 0 { return 0 }
172 let b: *u8 = sys_mmap(8); let r: i64 = sys_read(fd, b, 1); sys_close(fd)
173 if r > 0 { return 1 }
174 return 0
175}
176// Before exec in a forked child: close every fd the daemon held open (the listen socket + the
177// per-connection cfd + any transient fds). A long-lived worker must NEVER keep the client's
178// connection open -- if it did, an HTTP client that reads to EOF (rather than honoring
179// Content-Length) blocks until the whole download ends. The daemon's own gate hung exactly this
180// way. stdio (0,1,2 -> /dev/null) is kept; sys_close on an unopened fd returns EBADF, so the
181// blind 3..255 sweep is safe. This is the no-hang law extended to the client side.
182func d_close_inherited() -> i64 {
183 var fdx: i64 = 3
184 while fdx < 256 { sys_close(fdx); fdx = fdx + 1 }
185 return 0
186}
187// The directory THIS daemon binary lives in, read from /proc/self/cmdline argv[0]. The spawn elfs (worker,
188// discover, hls, video, ts2fmp4) are deployed as its SIBLINGS, so they resolve correctly on BOTH the dev box
189// (/tmp, where the WOMB builds them) and the NAS hub (/volume1/ai/torrent, where the bundle is deployed AND
190// /tmp is mounted NOEXEC -- the reason the old hardcoded /tmp exec failed on the NAS). Fallback "/tmp".
191func d_self_dir() -> *u8 {
192 let buf: *u8 = sys_mmap(TORRENT_MAGIC_1024)
193 let fd: i64 = sys_openat_rd("/proc/self/cmdline" as *u8)
194 var n: i64 = 0
195 if fd >= 0 { n = sys_read(fd, buf, 1023); sys_close(fd) }
196 if n <= 0 { let fb: *u8 = sys_mmap(8); fb[0]=47 as u8; fb[1]=116 as u8; fb[2]=109 as u8; fb[3]=112 as u8; fb[4]=0 as u8; return fb }
197 var z: i64 = 0; var dz: i64 = 0
198 while dz == 0 { if z >= n { dz = 1 } else { if buf[z] == (0 as u8) { dz = 1 } else { z = z + 1 } } }
199 var i: i64 = z - 1; var df: i64 = 0
200 while df == 0 { if i <= 0 { df = 1 } else { if buf[i] == (47 as u8) { buf[i] = 0 as u8; df = 1 } else { i = i - 1 } } }
201 if buf[0] == (0 as u8) { let fb: *u8 = sys_mmap(8); fb[0]=47 as u8; fb[1]=116 as u8; fb[2]=109 as u8; fb[3]=112 as u8; fb[4]=0 as u8; return fb }
202 return buf
203}
204func d_spawn_worker(magnet: *u8, outdir: *u8, resume: i64) -> i64 {
205 let wpath: *u8 = d_path(d_self_dir(), "/nx_torrent_get.sov.elf" as *u8, sys_mmap(640))
206 let argv: *i64 = sys_mmap(64) as *i64
207 argv[0] = wpath as i64; argv[1] = magnet as i64; argv[2] = outdir as i64
208 if resume == 1 { argv[3] = "r" as i64; argv[4] = 0 } else { argv[3] = 0 }
209 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = 0
210 let p1: i64 = sys_fork()
211 if p1 == 0 {
212 let p2: i64 = sys_fork()
213 if p2 == 0 { d_close_inherited(); sys_execve(wpath, argv, envp); sys_exit(127) }
214 sys_exit(0)
215 }
216 let st: *i64 = sys_mmap(16) as *i64
217 sys_wait4(p1, st, 0)
218 return 0
219}
220
221// ---- /status: parse the worker's newline-delimited status file -> JSON ----
222func d_find_val(buf: *u8, n: i64, key: *u8) -> i64 {
223 let kl: i64 = d_strlen(key)
224 var i: i64 = 0
225 while i + kl <= n {
226 var j: i64 = 0; var ok: i64 = 1
227 while j < kl { if buf[i+j] != key[j] { ok = 0; j = kl } else { j = j + 1 } }
228 if ok == 1 { return i + kl }
229 i = i + 1
230 }
231 return 0 - 1
232}
233func d_int_at(buf: *u8, n: i64, pos: i64) -> i64 {
234 if pos < 0 { return 0 }
235 var v: i64 = 0; var i: i64 = pos; var go: i64 = 1
236 while go == 1 {
237 if i >= n { go = 0 } else {
238 let c: i64 = buf[i] as i64
239 if c >= 48 { if c <= 57 { v = v*10 + (c-48); i = i+1 } else { go = 0 } } else { go = 0 }
240 }
241 }
242 return v
243}
244func d_copy_line(buf: *u8, n: i64, pos: i64, out: *u8, ocap: i64) -> i64 {
245 out[0] = 0 as u8
246 if pos < 0 { return 0 }
247 var i: i64 = pos; var k: i64 = 0; var go: i64 = 1
248 while go == 1 {
249 if i >= n { go = 0 } else {
250 if buf[i]==(10 as u8) { go = 0 } else {
251 if k < ocap-1 { out[k]=buf[i]; k=k+1 }
252 i = i+1
253 }
254 }
255 }
256 out[k] = 0 as u8
257 return k
258}
259func d_status(cfd: i64, statpath: *u8, ctlpath: *u8) -> i64 {
260 // operator control overrides (file-based, written by pause/remove): removed -> empty list,
261 // paused -> force the rendered state to "paused" (the worker has self-stopped).
262 let cb: *u8 = sys_mmap(16); let cn: i64 = d_read_file(ctlpath, cb, 15)
263 var removed: i64 = 0; var paused: i64 = 0
264 if cn > 0 { let c0: i64 = cb[0] as i64; if c0 == 114 { removed = 1 } if c0 == 112 { paused = 1 } }
265 let buf: *u8 = sys_mmap(TORRENT_MAGIC_8192)
266 let n: i64 = d_read_file(statpath, buf, TORRENT_MAGIC_8192)
267 let out: *u8 = sys_mmap(TORRENT_MAGIC_8192)
268 var o: i64 = 0
269 if removed == 1 {
270 o = d_puts(out, o, "{\"downloads\":[]}" as *u8)
271 d_send(cfd, "200 OK" as *u8, "application/json" as *u8, out, o); return 0
272 }
273 if n <= 0 {
274 o = d_puts(out, o, "{\"downloads\":[]}" as *u8)
275 d_send(cfd, "200 OK" as *u8, "application/json" as *u8, out, o); return 0
276 }
277 let total: i64 = d_int_at(buf, n, d_find_val(buf, n, "total=" as *u8))
278 let done: i64 = d_int_at(buf, n, d_find_val(buf, n, "done=" as *u8))
279 let have: i64 = d_int_at(buf, n, d_find_val(buf, n, "have=" as *u8))
280 let pieces: i64= d_int_at(buf, n, d_find_val(buf, n, "pieces=" as *u8))
281 let peers: i64 = d_int_at(buf, n, d_find_val(buf, n, "peers=" as *u8))
282 let stateb: *u8 = sys_mmap(64); d_copy_line(buf, n, d_find_val(buf, n, "state=" as *u8), stateb, 64)
283 if paused == 1 { var pz: i64 = 0; let ps2: *u8 = "paused" as *u8; while ps2[pz] != (0 as u8) { stateb[pz] = ps2[pz]; pz = pz + 1 } stateb[pz] = 0 as u8 }
284 let nameb: *u8 = sys_mmap(512); d_copy_line(buf, n, d_find_val(buf, n, "name=" as *u8), nameb, 512)
285 var vpct: i64 = 0; if pieces > 0 { vpct = have*100/pieces }
286 o = d_puts(out, o, "{\"downloads\":[{\"id\":\"1\",\"name\":\"" as *u8)
287 o = d_puts(out, o, nameb)
288 o = d_puts(out, o, "\",\"size\":" as *u8); o = d_putn(out, o, total)
289 o = d_puts(out, o, ",\"done\":" as *u8); o = d_putn(out, o, done)
290 o = d_puts(out, o, ",\"peers\":" as *u8); o = d_putn(out, o, peers)
291 o = d_puts(out, o, ",\"havePieces\":" as *u8); o = d_putn(out, o, have)
292 o = d_puts(out, o, ",\"totalPieces\":" as *u8); o = d_putn(out, o, pieces)
293 o = d_puts(out, o, ",\"verified\":" as *u8); o = d_putn(out, o, vpct)
294 o = d_puts(out, o, ",\"status\":\"" as *u8); o = d_puts(out, o, stateb)
295 o = d_puts(out, o, "\"}]}" as *u8)
296 d_send(cfd, "200 OK" as *u8, "application/json" as *u8, out, o)
297 return 0
298}
299
300// ---- discover: extract a page URL from the JSON body ----
301func d_extract_url(body: *u8, blen: i64, out: *u8, ocap: i64) -> i64 {
302 var i: i64 = 0; var start: i64 = 0 - 1
303 while i + 4 <= blen {
304 if body[i]==(104 as u8) { if body[i+1]==(116 as u8) { if body[i+2]==(116 as u8) { if body[i+3]==(112 as u8) {
305 start = i; i = blen + 100
306 }}}}
307 i = i + 1
308 }
309 if start < 0 { return 0 }
310 var j: i64 = start; var k: i64 = 0; var go: i64 = 1
311 while go == 1 {
312 if j >= blen { go = 0 } else {
313 let c: i64 = body[j] as i64
314 if c==34 { go=0 } else { if c==39 { go=0 } else { if c==32 { go=0 } else { if c==60 { go=0 } else { if c==62 { go=0 } else { if c==10 { go=0 } else { if c==13 { go=0 } else {
315 if k < ocap - 1 { out[k] = body[j]; k = k + 1 }
316 j = j + 1
317 }}}}}}}
318 }
319 }
320 out[k] = 0 as u8
321 return k
322}
323
324// spawn the discovery elf (double-fork, reparents to init)
325func d_spawn_discover(url: *u8, dpath: *u8) -> i64 {
326 let wpath: *u8 = d_path(d_self_dir(), "/nx_magnet_discover.sov.elf" as *u8, sys_mmap(640))
327 let argv: *i64 = sys_mmap(64) as *i64
328 argv[0] = wpath as i64; argv[1] = url as i64; argv[2] = dpath as i64; argv[3] = 0
329 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = 0
330 let p1: i64 = sys_fork()
331 if p1 == 0 {
332 let p2: i64 = sys_fork()
333 if p2 == 0 { d_close_inherited(); sys_execve(wpath, argv, envp); sys_exit(127) }
334 sys_exit(0)
335 }
336 let st: *i64 = sys_mmap(16) as *i64
337 sys_wait4(p1, st, 0)
338 return 0
339}
340
341// Build a same-origin Referer ("https://<host>/") from a media URL. Most CDNs gate direct .mp4 links on a
342// Referer header; the downloader already sends a browser UA, so an EMPTY referer is the #1 reason a
343// correctly-routed direct video still 403s. out = NUL-terminated referer (empty if the URL has no scheme).
344func d_url_referer(url: *u8, out: *u8) -> *u8 {
345 var i: i64 = 0; var n: i64 = 0; while url[n] != (0 as u8) { n = n + 1 }
346 var sce: i64 = 0 - 1
347 while i + 3 <= n { if url[i]==(58 as u8) { if url[i+1]==(47 as u8) { if url[i+2]==(47 as u8) { sce = i + 3; i = n + 100 } } } i = i + 1 }
348 if sce < 0 { out[0] = 0 as u8; return out }
349 var he: i64 = sce; var hgo: i64 = 1
350 while hgo == 1 { if he >= n { hgo = 0 } else { let c: i64 = url[he] as i64; if c==47 { hgo=0 } else { if c==58 { hgo=0 } else { if c==63 { hgo=0 } else { he = he + 1 } } } } }
351 var o: i64 = 0; var k: i64 = 0
352 while k < he { out[o] = url[k]; o = o + 1; k = k + 1 }
353 out[o] = 47 as u8; o = o + 1
354 out[o] = 0 as u8
355 return out
356}
357
358// spawn the sovereign video downloader (double-fork). argv: elf, url, outpath, referer.
359func d_spawn_video(url: *u8, outpath: *u8, referer: *u8) -> i64 {
360 let wpath: *u8 = d_path(d_self_dir(), "/nx_video_get.sov.elf" as *u8, sys_mmap(640))
361 let argv: *i64 = sys_mmap(64) as *i64
362 argv[0] = wpath as i64; argv[1] = url as i64; argv[2] = outpath as i64; argv[3] = referer as i64; argv[4] = 0
363 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = 0
364 let p1: i64 = sys_fork()
365 if p1 == 0 { let p2: i64 = sys_fork(); if p2 == 0 { d_close_inherited(); sys_execve(wpath, argv, envp); sys_exit(127) } sys_exit(0) }
366 let st: *i64 = sys_mmap(16) as *i64; sys_wait4(p1, st, 0)
367 return 0
368}
369// spawn the sovereign HLS downloader (double-fork). argv: elf, m3u8-url, outpath(.ts), referer.
370func d_spawn_hls(url: *u8, outpath: *u8, referer: *u8) -> i64 {
371 let wpath: *u8 = d_path(d_self_dir(), "/nx_hls_get.sov.elf" as *u8, sys_mmap(640))
372 let argv: *i64 = sys_mmap(64) as *i64
373 argv[0] = wpath as i64; argv[1] = url as i64; argv[2] = outpath as i64; argv[3] = referer as i64; argv[4] = 0
374 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = 0
375 let p1: i64 = sys_fork()
376 if p1 == 0 { let p2: i64 = sys_fork(); if p2 == 0 { d_close_inherited(); sys_execve(wpath, argv, envp); sys_exit(127) } sys_exit(0) }
377 let st: *i64 = sys_mmap(16) as *i64; sys_wait4(p1, st, 0)
378 return 0
379}
380// is this URL an HLS playlist? (path ends with .m3u8, case-insensitive, before any '?')
381func d_is_hls(url: *u8) -> i64 {
382 var n: i64 = 0; while url[n] != (0 as u8) { n = n + 1 }
383 var e: i64 = n; var i: i64 = 0
384 while i < n { if url[i] == (63 as u8) { e = i; i = n } else { i = i + 1 } } // stop at '?'
385 if e < 5 { return 0 }
386 let s: i64 = e - 5
387 if url[s] != (46 as u8) { return 0 } // '.'
388 var c0: i64 = url[s+1] as i64; if c0 >= 65 { if c0 <= 90 { c0 = c0 + 32 } }
389 var c1: i64 = url[s+2] as i64; if c1 >= 65 { if c1 <= 90 { c1 = c1 + 32 } }
390 var c2: i64 = url[s+3] as i64; if c2 >= 65 { if c2 <= 90 { c2 = c2 + 32 } }
391 var c3: i64 = url[s+4] as i64; if c3 >= 65 { if c3 <= 90 { c3 = c3 + 32 } }
392 if c0 != 109 { return 0 } // m
393 if c1 != 51 { return 0 } // 3
394 if c2 != 117 { return 0 } // u
395 if c3 != 56 { return 0 } // 8
396 return 1
397}
398// HLS output name: basename (up to the first '.') sanitized, then a forced .ts (stitched MPEG-TS).
399func d_hls_outname(url: *u8, outdir: *u8, out: *u8) -> *u8 {
400 var ulen: i64 = 0; while url[ulen] != (0 as u8) { ulen = ulen + 1 }
401 var qend: i64 = ulen; var qi: i64 = 0; while qi < ulen { if url[qi] == (63 as u8) { qend = qi; qi = ulen } else { qi = qi + 1 } }
402 var seg: i64 = 0; var si: i64 = 0; while si < qend { if url[si] == (47 as u8) { seg = si + 1 } si = si + 1 }
403 var o: i64 = d_puts(out, 0, outdir); o = d_puts(out, o, "/" as *u8)
404 var nz: i64 = 0; var k: i64 = seg; var stop: i64 = 0
405 while k < qend { if stop == 0 {
406 let c: i64 = url[k] as i64
407 if c == 46 { stop = 1 } else { var ok: i64 = 0; if c>=48 { if c<=57 { ok=1 } } if c>=65 { if c<=90 { ok=1 } } if c>=97 { if c<=122 { ok=1 } } if c==45 { ok=1 } if c==95 { ok=1 } if ok==1 { out[o]=url[k]; o=o+1; nz=nz+1 } }
408 } k = k + 1 }
409 if nz == 0 { o = d_puts(out, o, "video" as *u8) }
410 o = d_puts(out, o, ".ts" as *u8)
411 out[o] = 0 as u8
412 return out
413}
414// derive outdir/<basename> from a video URL (last path segment before '?'; sanitized; fallback video.mp4).
415func d_vidname(url: *u8, outdir: *u8, out: *u8) -> *u8 {
416 var ulen: i64 = 0; while url[ulen] != (0 as u8) { ulen = ulen + 1 }
417 var qend: i64 = ulen; var qi: i64 = 0; while qi < ulen { if url[qi] == (63 as u8) { qend = qi; qi = ulen } else { qi = qi + 1 } }
418 var seg: i64 = 0; var si: i64 = 0; while si < qend { if url[si] == (47 as u8) { seg = si + 1 } si = si + 1 }
419 var o: i64 = d_puts(out, 0, outdir); o = d_puts(out, o, "/" as *u8)
420 var nz: i64 = 0; var k: i64 = seg
421 while k < qend { let c: i64 = url[k] as i64; var ok: i64 = 0; if c>=48 { if c<=57 { ok=1 } } if c>=65 { if c<=90 { ok=1 } } if c>=97 { if c<=122 { ok=1 } } if c==46 { ok=1 } if c==45 { ok=1 } if c==95 { ok=1 } if ok==1 { out[o]=url[k]; o=o+1; nz=nz+1 } k=k+1 }
422 if nz == 0 { o = d_puts(out, o, "video.mp4" as *u8) }
423 out[o] = 0 as u8
424 return out
425}
426
427func d_scan_to(buf: *u8, n: i64, from: i64, delim: i64) -> i64 {
428 var i: i64 = from
429 while i < n { if (buf[i] as i64) == delim { return i } i = i + 1 }
430 return n
431}
432func d_json_buf(dst: *u8, off: i64, src: *u8, from: i64, to: i64) -> i64 {
433 var i: i64 = from
434 while i < to {
435 let c: i64 = src[i] as i64
436 if c==34 { dst[off]=92 as u8; off=off+1; dst[off]=34 as u8; off=off+1 }
437 else { if c==92 { dst[off]=92 as u8; off=off+1; dst[off]=92 as u8; off=off+1 }
438 else { dst[off]=src[i]; off=off+1 } }
439 i = i + 1
440 }
441 return off
442}
443func d_raw_buf(dst: *u8, off: i64, src: *u8, from: i64, to: i64) -> i64 {
444 if from >= to { dst[off]=48 as u8; return off+1 }
445 var i: i64 = from; while i < to { dst[off]=src[i]; off=off+1; i=i+1 }
446 return off
447}
448// GET /discover_result -> read discover.tsv (+ .status) -> JSON
449func d_discover_result(cfd: i64, dpath: *u8) -> i64 {
450 let sp: *u8 = sys_mmap(TORRENT_MAGIC_1024); var i: i64 = 0; while dpath[i]!=(0 as u8){sp[i]=dpath[i];i=i+1}
451 let suf: *u8 = ".status" as *u8; var j: i64 = 0; while suf[j]!=(0 as u8){sp[i]=suf[j];i=i+1;j=j+1} sp[i]=0 as u8
452 let sbuf: *u8 = sys_mmap(TORRENT_MAGIC_1024); let sn: i64 = d_read_file(sp, sbuf, TORRENT_MAGIC_1024)
453 let stateb: *u8 = sys_mmap(64); stateb[0]=0 as u8
454 var scount: i64 = 0; var sbytes: i64 = 0
455 if sn > 0 {
456 d_copy_line(sbuf, sn, d_find_val(sbuf, sn, "state=" as *u8), stateb, 64)
457 scount = d_int_at(sbuf, sn, d_find_val(sbuf, sn, "count=" as *u8))
458 sbytes = d_int_at(sbuf, sn, d_find_val(sbuf, sn, "bytes=" as *u8))
459 } else { let df: *u8 = "fetching" as *u8; var z: i64 = 0; while df[z]!=(0 as u8){stateb[z]=df[z];z=z+1} stateb[z]=0 as u8 }
460 let tbuf: *u8 = sys_mmap(TORRENT_MAGIC_262144); let tn: i64 = d_read_file(dpath, tbuf, TORRENT_MAGIC_262144)
461 let out: *u8 = sys_mmap(TORRENT_MAGIC_262144); var o: i64 = 0
462 o = d_puts(out, o, "{\"state\":\"" as *u8); o = d_json_buf(out, o, stateb, 0, d_strlen(stateb))
463 o = d_puts(out, o, "\",\"count\":" as *u8); o = d_putn(out, o, scount)
464 o = d_puts(out, o, ",\"bytes\":" as *u8); o = d_putn(out, o, sbytes)
465 o = d_puts(out, o, ",\"magnets\":[" as *u8)
466 if tn > 0 {
467 var p: i64 = 0; var first: i64 = 1; var guard: i64 = 0
468 while p < tn {
469 if guard > TORRENT_MAGIC_4000 { p = tn } else {
470 guard = guard + 1
471 let a: i64 = d_scan_to(tbuf, tn, p, 9)
472 let b: i64 = d_scan_to(tbuf, tn, a+1, 9)
473 let c: i64 = d_scan_to(tbuf, tn, b+1, 9)
474 let e: i64 = d_scan_to(tbuf, tn, c+1, 10)
475 if a < tn {
476 if first == 0 { o = d_puts(out, o, "," as *u8) } first = 0
477 o = d_puts(out, o, "{\"hash\":\"" as *u8); o = d_json_buf(out, o, tbuf, p, a)
478 o = d_puts(out, o, "\",\"name\":\"" as *u8); o = d_json_buf(out, o, tbuf, a+1, b)
479 o = d_puts(out, o, "\",\"trackers\":" as *u8); o = d_raw_buf(out, o, tbuf, b+1, c)
480 o = d_puts(out, o, ",\"magnet\":\"" as *u8); o = d_json_buf(out, o, tbuf, c+1, e)
481 o = d_puts(out, o, "\"}" as *u8)
482 }
483 p = e + 1
484 }
485 }
486 }
487 o = d_puts(out, o, "]}" as *u8)
488 d_send(cfd, "200 OK" as *u8, "application/json" as *u8, out, o)
489 return 0
490}
491
492// ============ MULTI-TORRENT: per-info-hash isolation + a registry ============
493// Each torrent lives in outdir/<btih>/download.* (the worker already takes its outdir).
494// outdir/torrents.idx is a newline-list of active <btih> ids. No clobber between torrents.
495
496// extract the btih value from a magnet ("...btih:<id>&..." or end) -> the per-torrent id (hex/base32)
497func d_extract_btih(mag: *u8, n: i64, out: *u8, ocap: i64) -> i64 {
498 var i: i64 = 0; var start: i64 = 0 - 1
499 while i + 5 <= n {
500 if mag[i]==(98 as u8) { if mag[i+1]==(116 as u8) { if mag[i+2]==(105 as u8) { if mag[i+3]==(104 as u8) { if mag[i+4]==(58 as u8) {
501 start = i + 5; i = n + 100
502 }}}}}
503 i = i + 1
504 }
505 if start < 0 { return 0 }
506 var j: i64 = start; var k: i64 = 0; var go: i64 = 1
507 while go == 1 {
508 if j >= n { go = 0 } else { if mag[j]==(38 as u8) { go = 0 } else {
509 if k < ocap - 1 { out[k] = mag[j]; k = k + 1 }
510 j = j + 1
511 } }
512 }
513 out[k] = 0 as u8
514 return k
515}
516// hex char -> 0..15 (else -1), for %XX url-decode in dn= names
517func d_hexv(c: i64) -> i64 {
518 if c >= 48 { if c <= 57 { return c - 48 } }
519 if c >= 97 { if c <= 102 { return c - 97 + 10 } }
520 if c >= 65 { if c <= 70 { return c - 65 + 10 } }
521 return 0 - 1
522}
523// extract dn= (display name) from the magnet, url-decode (%XX, '+'), make filesystem+URL-safe -> out.
524// Keeps [A-Za-z0-9._-]; every run of other chars collapses to a single '_' (never leading). So the user
525// sees a recognizable folder ("javdb.com_326FCT-218") in Downloads instead of a raw info-hash. 0 = no dn=.
526func d_extract_name(mag: *u8, n: i64, out: *u8, ocap: i64) -> i64 {
527 var i: i64 = 0; var start: i64 = 0 - 1
528 while i + 3 <= n {
529 if mag[i]==(100 as u8) { if mag[i+1]==(110 as u8) { if mag[i+2]==(61 as u8) { start = i + 3; i = n + 100 } } } // 'd','n','='
530 i = i + 1
531 }
532 if start < 0 { return 0 }
533 var j: i64 = start; var k: i64 = 0; var pendu: i64 = 0; var go: i64 = 1
534 while go == 1 {
535 if j >= n { go = 0 } else { if mag[j]==(38 as u8) { go = 0 } else {
536 var c: i64 = mag[j] as i64
537 if c == 37 { if j + 2 < n { let hi: i64 = d_hexv(mag[j+1] as i64); let lo: i64 = d_hexv(mag[j+2] as i64); if hi >= 0 { if lo >= 0 { c = hi*16+lo; j = j + 2 } } } }
538 if c == 43 { c = 32 }
539 var keep: i64 = 0
540 if c >= 48 { if c <= 57 { keep = 1 } }
541 if c >= 65 { if c <= 90 { keep = 1 } }
542 if c >= 97 { if c <= 122 { keep = 1 } }
543 if c == 46 { keep = 1 }
544 if c == 45 { keep = 1 }
545 if c == 95 { keep = 1 }
546 if keep == 1 {
547 if pendu == 1 { if k < ocap - 1 { out[k] = 95 as u8; k = k + 1 } pendu = 0 }
548 if k < ocap - 1 { out[k] = c as u8; k = k + 1 }
549 } else { if k > 0 { pendu = 1 } }
550 j = j + 1
551 } }
552 }
553 out[k] = 0 as u8
554 return k
555}
556// build outdir/<id> into dir (NUL-terminated); returns dir.
557func d_subdir(outdir: *u8, id: *u8, dir: *u8) -> *u8 {
558 var p: i64 = d_puts(dir, 0, outdir); p = d_puts(dir, p, "/" as *u8); p = d_puts(dir, p, id); dir[p] = 0 as u8
559 return dir
560}
561func d_ends(buf: *u8, off: i64, len: i64, suf: *u8) -> i64 {
562 let sl: i64 = d_strlen(suf)
563 if sl > len { return 0 }
564 var i: i64 = 0
565 while i < sl { if buf[off+len-sl+i] != suf[i] { return 0 } i = i + 1 }
566 return 1
567}
568// is id one of the newline-delimited lines in buf[0..n)?
569func d_idx_has(buf: *u8, n: i64, id: *u8) -> i64 {
570 let idl: i64 = d_strlen(id)
571 var i: i64 = 0
572 while i < n {
573 let e: i64 = d_scan_to(buf, n, i, 10)
574 if (e - i) == idl { var j: i64 = 0; var m: i64 = 1; while j < idl { if buf[i+j] != id[j] { m = 0; j = idl } else { j = j + 1 } } if m == 1 { return 1 } }
575 i = e + 1
576 }
577 return 0
578}
579// add id to the registry if absent (rewrite the small file; idempotent)
580func d_idx_add(idxpath: *u8, id: *u8) -> i64 {
581 // best-effort dedup, then ATOMIC O_APPEND -- not read-modify-write. Two simultaneous adds used to
582 // both rewrite the whole file, losing one entry (the gate's false-RED); O_APPEND can't clobber.
583 let buf: *u8 = sys_mmap(TORRENT_MAGIC_65536); let n: i64 = d_read_file(idxpath, buf, TORRENT_MAGIC_65536); if n > 0 { if d_idx_has(buf, n, id) == 1 { return 0 } }
584 let fd: i64 = __syscall(SYS_OPENAT, AT_FDCWD, idxpath as i64, O_WRONLY_CA, 0x1a4, 0, 0)
585 if fd < 0 { return 0 - 1 }
586 let line: *u8 = sys_mmap(160); var k: i64 = 0; while id[k] != (0 as u8) { line[k] = id[k]; k = k + 1 } line[k] = 10 as u8; k = k + 1
587 sys_write(fd, line, k)
588 sys_close(fd)
589 return 0
590}
591// drop id's line from the registry (rewrite without it)
592func d_idx_remove(idxpath: *u8, id: *u8) -> i64 {
593 let buf: *u8 = sys_mmap(TORRENT_MAGIC_65536); let n: i64 = d_read_file(idxpath, buf, TORRENT_MAGIC_65536); if n <= 0 { return 0 }
594 let idl: i64 = d_strlen(id)
595 let out: *u8 = sys_mmap(TORRENT_MAGIC_65536); var o: i64 = 0
596 var i: i64 = 0
597 while i < n {
598 let e: i64 = d_scan_to(buf, n, i, 10)
599 let len: i64 = e - i
600 var keep: i64 = 1
601 if len == idl { var j: i64 = 0; var m: i64 = 1; while j < idl { if buf[i+j] != id[j] { m = 0; j = idl } else { j = j + 1 } } if m == 1 { keep = 0 } }
602 if keep == 1 { if len > 0 { var c: i64 = i; while c < e { out[o] = buf[c]; o = o + 1; c = c + 1 } out[o] = 10 as u8; o = o + 1 } }
603 i = e + 1
604 }
605 d_write_file(idxpath, out, o)
606 return 0
607}
608// append ONE Download JSON object (id + its dir's status, control-overridden) to out at o0; return new o.
609func d_status_one(out: *u8, o0: i64, id: *u8, dir: *u8) -> i64 {
610 var o: i64 = o0
611 let statpath: *u8 = d_path(dir, "/download.status" as *u8, sys_mmap(640))
612 let ctl2: *u8 = d_path(dir, "/download.control" as *u8, sys_mmap(640))
613 let cb: *u8 = sys_mmap(16); let cn: i64 = d_read_file(ctl2, cb, 15)
614 var paused: i64 = 0; if cn > 0 { if (cb[0] as i64) == 112 { paused = 1 } }
615 let buf: *u8 = sys_mmap(TORRENT_MAGIC_8192); let n: i64 = d_read_file(statpath, buf, TORRENT_MAGIC_8192)
616 let total: i64 = d_int_at(buf, n, d_find_val(buf, n, "total=" as *u8))
617 let done: i64 = d_int_at(buf, n, d_find_val(buf, n, "done=" as *u8))
618 let have: i64 = d_int_at(buf, n, d_find_val(buf, n, "have=" as *u8))
619 let pieces: i64= d_int_at(buf, n, d_find_val(buf, n, "pieces=" as *u8))
620 let peers: i64 = d_int_at(buf, n, d_find_val(buf, n, "peers=" as *u8))
621 let stateb: *u8 = sys_mmap(64); d_copy_line(buf, n, d_find_val(buf, n, "state=" as *u8), stateb, 64)
622 if n <= 0 { var z: i64 = 0; let s0: *u8 = "started" as *u8; while s0[z] != (0 as u8) { stateb[z] = s0[z]; z = z + 1 } stateb[z] = 0 as u8 }
623 if paused == 1 { var z2: i64 = 0; let s1: *u8 = "paused" as *u8; while s1[z2] != (0 as u8) { stateb[z2] = s1[z2]; z2 = z2 + 1 } stateb[z2] = 0 as u8 }
624 let nameb: *u8 = sys_mmap(512); d_copy_line(buf, n, d_find_val(buf, n, "name=" as *u8), nameb, 512)
625 if n <= 0 { var z3: i64 = 0; let s2: *u8 = "resolving" as *u8; while s2[z3] != (0 as u8) { nameb[z3] = s2[z3]; z3 = z3 + 1 } nameb[z3] = 0 as u8 }
626 var vpct: i64 = 0; if pieces > 0 { vpct = have*100/pieces }
627 o = d_puts(out, o, "{\"id\":\"" as *u8); o = d_puts(out, o, id)
628 o = d_puts(out, o, "\",\"name\":\"" as *u8); o = d_json_buf(out, o, nameb, 0, d_strlen(nameb))
629 o = d_puts(out, o, "\",\"size\":" as *u8); o = d_putn(out, o, total)
630 o = d_puts(out, o, ",\"done\":" as *u8); o = d_putn(out, o, done)
631 o = d_puts(out, o, ",\"peers\":" as *u8); o = d_putn(out, o, peers)
632 o = d_puts(out, o, ",\"havePieces\":" as *u8); o = d_putn(out, o, have)
633 o = d_puts(out, o, ",\"totalPieces\":" as *u8); o = d_putn(out, o, pieces)
634 o = d_puts(out, o, ",\"verified\":" as *u8); o = d_putn(out, o, vpct)
635 o = d_puts(out, o, ",\"status\":\"" as *u8); o = d_puts(out, o, stateb)
636 o = d_puts(out, o, "\"}" as *u8)
637 return o
638}
639// GET /status: aggregate every torrent in the registry into the downloads array.
640func d_status_all(cfd: i64, outdir: *u8, idxpath: *u8) -> i64 {
641 let idx: *u8 = sys_mmap(TORRENT_MAGIC_65536); let n: i64 = d_read_file(idxpath, idx, TORRENT_MAGIC_65536)
642 let out: *u8 = sys_mmap(TORRENT_MAGIC_262144); var o: i64 = 0
643 o = d_puts(out, o, "{\"downloads\":[" as *u8)
644 var first: i64 = 1
645 if n > 0 {
646 var i: i64 = 0; var guard: i64 = 0
647 while i < n {
648 if guard > TORRENT_MAGIC_2000 { i = n } else {
649 guard = guard + 1
650 let e: i64 = d_scan_to(idx, n, i, 10)
651 let len: i64 = e - i
652 if len > 0 {
653 let id: *u8 = sys_mmap(128); var k: i64 = 0; while k < len { if k < 127 { id[k] = idx[i+k]; k = k + 1 } else { k = len } } id[len] = 0 as u8
654 if d_idx_has(idx, i, id) == 0 { // dedup: O_APPEND may leave a rare dup line; render each id once
655 let dir: *u8 = d_subdir(outdir, id, sys_mmap(640))
656 if first == 0 { o = d_puts(out, o, "," as *u8) } first = 0
657 o = d_status_one(out, o, id, dir)
658 }
659 }
660 i = e + 1
661 }
662 }
663 }
664 o = d_puts(out, o, "]}" as *u8)
665 d_send(cfd, "200 OK" as *u8, "application/json" as *u8, out, o)
666 return 0
667}
668
669// ---- stream-while-downloading: serve the VIDEO file's downloaded prefix from download.part (HTTP Range) ----
670// The torrent's LARGEST file is the video; its bytes live at [voff, voff+vlen) inside download.part. We serve
671// the contiguous-downloaded intersection so a <video> plays what has arrived. Works fully for a completed
672// download (streams straight from download.part, no extraction) and progressively in sequential stream-mode.
673
674// largest file's (offset,length) within download.part, from download.meta. out[0]=offset, out[1]=length.
675func d_video_extent(meta: *u8, msize: i64, out: *i64) -> i64 {
676 out[0] = 0; out[1] = 0
677 let filo: i64 = nx_bc_dict_get(meta, 0, msize, "files" as *u8, 5)
678 if filo >= 0 { if (meta[filo] as i64) == 0x6C { // 'l' -> multi-file
679 var p: i64 = filo + 1; var off: i64 = 0; var bestoff: i64 = 0; var bestlen: i64 = 0; var run: i64 = 1
680 while run == 1 {
681 if p >= msize { run = 0 } else { if (meta[p] as i64) == 0x65 { run = 0 } else {
682 let lo: i64 = nx_bc_dict_get(meta, p, msize, "length" as *u8, 6); let lv: *i64 = sys_mmap(8) as *i64; lv[0]=0; if lo>=0 { nx_bc_int(meta, lo, msize, lv) }
683 if lv[0] > bestlen { bestlen = lv[0]; bestoff = off }
684 off = off + lv[0]; p = nx_bc_skip(meta, p, msize)
685 } }
686 }
687 out[0] = bestoff; out[1] = bestlen; return 1
688 } }
689 let lno: i64 = nx_bc_dict_get(meta, 0, msize, "length" as *u8, 6); let lv2: *i64 = sys_mmap(8) as *i64; lv2[0]=0; if lno>=0 { nx_bc_int(meta, lno, msize, lv2) }
690 out[0] = 0; out[1] = lv2[0]; return 1 // single-file -> whole part is the file
691}
692
693// contiguous downloaded bytes from offset 0 (full pieces only) = read download.done + download.npc.
694func d_contig_bytes(dir: *u8) -> i64 {
695 let donep: *u8 = d_path(dir, "/download.done" as *u8, sys_mmap(640))
696 let db: *u8 = sys_mmap(TORRENT_MAGIC_262144); let dn: i64 = d_read_file(donep, db, TORRENT_MAGIC_262144)
697 if dn <= 0 { return 0 }
698 var contig: i64 = 0; var i: i64 = 0
699 while i < dn { if (db[i] as i64) == 1 { contig = contig + 1; i = i + 1 } else { i = dn } }
700 let npcp: *u8 = d_path(dir, "/download.npc" as *u8, sys_mmap(640))
701 let nb: *u8 = sys_mmap(16); let nn: i64 = d_read_file(npcp, nb, 16)
702 var plen: i64 = 0
703 if nn >= 8 { plen = ((nb[4] as i64)<<24)|((nb[5] as i64)<<16)|((nb[6] as i64)<<8)|(nb[7] as i64) }
704 return contig * plen
705}
706
707// extract + url-decode the urlencoded "magnet=" form field (the browser UI sends
708// magnet=<encodeURIComponent(...)>&area=...). %XX -> byte, '+' -> space; the value runs to the field
709// separator '&' or end. The magnet's OWN '&' (between dn=/tr=) arrive as %26, so they do NOT terminate
710// early. Returns the decoded length, or 0 if there's no magnet= field / it isn't a magnet (caller then
711// falls back to the JSON/raw scan the gate uses). FIXES: browser "Add" returning 400 "no magnet".
712func d_form_magnet(body: *u8, blen: i64, out: *u8, ocap: i64) -> i64 {
713 var i: i64 = 0; var start: i64 = 0 - 1
714 while i + 7 <= blen {
715 if body[i]==(109 as u8){ if body[i+1]==(97 as u8){ if body[i+2]==(103 as u8){ if body[i+3]==(110 as u8){ if body[i+4]==(101 as u8){ if body[i+5]==(116 as u8){ if body[i+6]==(61 as u8){
716 start = i + 7; i = blen + 100
717 }}}}}}}
718 i = i + 1
719 }
720 if start < 0 { return 0 }
721 var j: i64 = start; var k: i64 = 0; var go: i64 = 1
722 while go == 1 {
723 if j >= blen { go = 0 } else {
724 var c: i64 = body[j] as i64
725 if c == 38 { go = 0 } else { // '&' = end of this form field
726 if c == 37 { if j + 2 < blen { let hi: i64 = d_hexv(body[j+1] as i64); let lo: i64 = d_hexv(body[j+2] as i64); if hi >= 0 { if lo >= 0 { c = hi*16 + lo; j = j + 2 } } } }
727 if c == 43 { c = 32 } // '+' -> space
728 if k < ocap - 1 { out[k] = c as u8; k = k + 1 }
729 j = j + 1
730 }
731 }
732 }
733 out[k] = 0 as u8
734 if k < 7 { return 0 } // must be at least "magnet:"
735 if out[0]!=(109 as u8){ return 0 } if out[1]!=(97 as u8){ return 0 } if out[2]!=(103 as u8){ return 0 } if out[3]!=(110 as u8){ return 0 } if out[4]!=(101 as u8){ return 0 } if out[5]!=(116 as u8){ return 0 } if out[6]!=(58 as u8){ return 0 }
736 return k
737}
738
739// size in bytes of a file (st_size @ off 48 of the x86_64 struct stat; mirrors the mtime@88 read in
740// d_autoresume). 0 if it doesn't exist. Lets /stream serve a (near-)complete video's full extent.
741func d_filesize(path: *u8) -> i64 {
742 let stbuf: *u8 = sys_mmap(160)
743 if sys_fstatat(path, stbuf) == 0 { let szp: *i64 = (((stbuf as i64) + 48)) as *i64; return szp[0] }
744 return 0
745}
746
747// out[0] = pieces verified on disk (count of 1-bytes in download.done), out[1] = total pieces (download.npc).
748func d_have_npc(dir: *u8, out: *i64) -> i64 {
749 out[0] = 0; out[1] = 0
750 let donep: *u8 = d_path(dir, "/download.done" as *u8, sys_mmap(640))
751 let db: *u8 = sys_mmap(TORRENT_MAGIC_262144); let dn: i64 = d_read_file(donep, db, TORRENT_MAGIC_262144)
752 var have: i64 = 0; var i: i64 = 0
753 while i < dn { if (db[i] as i64) == 1 { have = have + 1 } i = i + 1 }
754 out[0] = have
755 let npcp: *u8 = d_path(dir, "/download.npc" as *u8, sys_mmap(640))
756 let nb: *u8 = sys_mmap(16); let nn: i64 = d_read_file(npcp, nb, 16)
757 if nn >= 4 { out[1] = ((nb[0] as i64)<<24)|((nb[1] as i64)<<16)|((nb[2] as i64)<<8)|(nb[3] as i64) }
758 return 0
759}
760
761// parse "Range: bytes=START-END" (END optional). out[0]=start, out[1]=end(-1 open). returns 1 if present.
762func d_parse_range(req: *u8, rn: i64, out: *i64) -> i64 {
763 out[0] = 0; out[1] = 0 - 1
764 let pat: *u8 = "Range: bytes=" as *u8; let pl: i64 = 13
765 var i: i64 = 0; var pos: i64 = 0 - 1
766 while i + pl <= rn { var j: i64 = 0; var m: i64 = 1; while j < pl { if req[i+j] != pat[j] { m = 0; j = pl } else { j = j + 1 } } if m == 1 { pos = i + pl; i = rn } else { i = i + 1 } }
767 if pos < 0 { return 0 }
768 var s: i64 = 0; var sgo: i64 = 1
769 while sgo == 1 { if pos >= rn { sgo = 0 } else { let c: i64 = req[pos] as i64; if c >= 48 { if c <= 57 { s = s*10 + (c-48); pos = pos + 1 } else { sgo = 0 } } else { sgo = 0 } } }
770 out[0] = s // pos now points at the first non-digit (the '-') or rn
771 if pos < rn { if (req[pos] as i64) == 45 { // '-'
772 pos = pos + 1; var e: i64 = 0; var he: i64 = 0
773 while pos < rn { let c2: i64 = req[pos] as i64; if c2 >= 48 { if c2 <= 57 { e = e*10 + (c2-48); he = 1; pos = pos + 1 } else { pos = rn } } else { pos = rn } }
774 if he == 1 { out[1] = e }
775 } }
776 return 1
777}
778
779func d_stream(cfd: i64, req: *u8, rn: i64, dir: *u8) -> i64 {
780 // a /stream request means the operator is watching -> switch this torrent to SEQUENTIAL download so
781 // the playable prefix grows in order (the worker reads download.stream each pick). Harmless if complete.
782 d_write_file(d_path(dir, "/download.stream" as *u8, sys_mmap(640)), "1" as *u8, 1)
783 let metap: *u8 = d_path(dir, "/download.meta" as *u8, sys_mmap(640))
784 let meta: *u8 = sys_mmap(TORRENT_MAGIC_1048576); let msize: i64 = d_read_file(metap, meta, TORRENT_MAGIC_1048576)
785 if msize <= 0 { d_send_str(cfd, "404 Not Found" as *u8, "text/plain" as *u8, "no metadata yet\n" as *u8); return 0 }
786 let ext: *i64 = sys_mmap(16) as *i64; d_video_extent(meta, msize, ext)
787 let voff: i64 = ext[0]; let vlen: i64 = ext[1]
788 if vlen <= 0 { d_send_str(cfd, "404 Not Found" as *u8, "text/plain" as *u8, "no playable file\n" as *u8); return 0 }
789 // AVAIL = how much of the video we can serve now. Near-complete or complete (>=90% of pieces) -> serve
790 // the whole furthest-downloaded extent of download.part (any still-missing piece is a sparse-zero gap the
791 // player skips), so a 99%-done OR finished torrent IS watchable. Early (<90%) -> only the contiguous
792 // prefix from byte 0, so sequential stream-mode plays cleanly from the start. FIXES "can't watch 99%
793 // done" + lets you watch one torrent while others keep downloading.
794 let hn: *i64 = sys_mmap(16) as *i64; d_have_npc(dir, hn)
795 let have: i64 = hn[0]; let npc: i64 = hn[1]
796 // COMPLETE single-file MPEG-TS -> fly-remux to fMP4 so a plain <video> (Chrome/Firefox) plays it,
797 // mirroring the gallery's gs_vid_remux_ts (rung 8). In-progress / multi-file fall through to the partial Range
798 // serve below (watch-while-downloading + native mp4). The daemon forks per-connection, so execve here
799 // replaces only this child.
800 if npc > 0 { if have >= npc { if voff == 0 {
801 let partp0: *u8 = d_path(dir, "/download.part" as *u8, sys_mmap(640))
802 let mb0: *u8 = sys_mmap(64); let pf0: i64 = sys_openat_rd(partp0); var g0: i64 = 0; if pf0 >= 0 { g0 = sys_read(pf0, mb0, 16) }
803 var is_ts: i64 = 0; if g0 >= 1 { if (mb0[0] as i64) == 0x47 { is_ts = 1 } } // 0x47 = MPEG-TS sync byte
804 var is_ftyp: i64 = 0; if g0 >= 16 { if mb0[4]==(102 as u8) { if mb0[5]==(116 as u8) { if mb0[6]==(121 as u8) { if mb0[7]==(112 as u8) { is_ftyp = 1 } } } } } // 'ftyp' = mp4
805 var is_fast: i64 = 0
806 if is_ftyp == 1 { // is the box AFTER ftyp already 'moov' (faststart)? then the Range serve below is seekable.
807 let ftsz: i64 = ((mb0[0] as i64)<<24)|((mb0[1] as i64)<<16)|((mb0[2] as i64)<<8)|(mb0[3] as i64)
808 let nb: *u8 = sys_mmap(16); var ng: i64 = 0; if pf0 >= 0 { sys_lseek(pf0, ftsz, 0); ng = sys_read(pf0, nb, 8) }
809 if ng >= 8 { if nb[4]==(109 as u8) { if nb[5]==(111 as u8) { if nb[6]==(111 as u8) { if nb[7]==(118 as u8) { is_fast = 1 } } } } } // 'moov'
810 }
811 if pf0 >= 0 { sys_close(pf0) }
812 // COMPLETE single-file MPEG-TS -> fly-remux to fMP4 so a plain <video> (Chrome/Firefox) plays it.
813 if is_ts == 1 {
814 let fh: *u8 = "HTTP/1.1 200 OK\r\nContent-Type: video/mp4\r\nCache-Control: no-store\r\nConnection: close\r\n\r\n" as *u8
815 sys_write(cfd, fh, d_strlen(fh)); sys_dup3(cfd, 1, 0)
816 let wexe: *u8 = d_path(d_self_dir(), "/nx_ts2fmp4.sov.elf" as *u8, sys_mmap(640)) // sibling of the daemon (self-dir): /tmp on dev, /volume1/ai/torrent on the NAS
817 let av2: *i64 = sys_mmap(64) as *i64; av2[0] = wexe as i64; av2[1] = partp0 as i64; av2[2] = 0
818 let ev2: *i64 = sys_mmap(16) as *i64; ev2[0] = 0
819 sys_execve(wexe, av2, ev2); sys_exit(127)
820 }
821 // NON-FASTSTART mp4 ([ftyp][mdat..][moov]): a browser streaming via Range reads the front, finds no moov
822 // -> "no video, this format is not supported". Relocate moov to the FRONT on the fly (nx_mp4_faststart,
823 // lossless, gate-proven) so a plain <video> plays it. Faststart mp4s (moov already front) fall through
824 // to the seekable Range serve below.
825 if is_ftyp == 1 { if is_fast == 0 {
826 let fh2: *u8 = "HTTP/1.1 200 OK\r\nContent-Type: video/mp4\r\nCache-Control: no-store\r\nConnection: close\r\n\r\n" as *u8
827 sys_write(cfd, fh2, d_strlen(fh2)); sys_dup3(cfd, 1, 0)
828 let wexe2: *u8 = d_path(d_self_dir(), "/nx_mp4_faststart.sov.elf" as *u8, sys_mmap(640))
829 let av3: *i64 = sys_mmap(64) as *i64; av3[0] = wexe2 as i64; av3[1] = partp0 as i64; av3[2] = 0
830 let ev3: *i64 = sys_mmap(16) as *i64; ev3[0] = 0
831 sys_execve(wexe2, av3, ev3); sys_exit(127)
832 } }
833 } } }
834 var avail: i64 = 0
835 if npc > 0 { if have * 100 >= npc * 90 {
836 let partp0: *u8 = d_path(dir, "/download.part" as *u8, sys_mmap(640))
837 avail = d_filesize(partp0) - voff
838 } else { avail = d_contig_bytes(dir) - voff } }
839 else { avail = d_contig_bytes(dir) - voff }
840 if avail < 0 { avail = 0 } if avail > vlen { avail = vlen }
841 if avail <= 0 { d_send_str(cfd, "425 Too Early" as *u8, "text/plain" as *u8, "not enough downloaded yet\n" as *u8); return 0 }
842 let rng: *i64 = sys_mmap(16) as *i64; d_parse_range(req, rn, rng)
843 var rs: i64 = rng[0]; var re: i64 = rng[1]
844 if re < 0 { re = vlen - 1 }
845 if rs < 0 { rs = 0 }
846 if rs >= avail { d_send_str(cfd, "416 Range Not Satisfiable" as *u8, "text/plain" as *u8, "beyond available\n" as *u8); return 0 }
847 if re > avail - 1 { re = avail - 1 }
848 let served: i64 = re - rs + 1
849 let h: *u8 = sys_mmap(512); var o: i64 = 0
850 o = d_puts(h, o, "HTTP/1.1 206 Partial Content\r\nContent-Type: video/mp4\r\nAccept-Ranges: bytes\r\nContent-Range: bytes " as *u8)
851 o = d_putn(h, o, rs); o = d_puts(h, o, "-" as *u8); o = d_putn(h, o, re); o = d_puts(h, o, "/" as *u8); o = d_putn(h, o, vlen)
852 o = d_puts(h, o, "\r\nContent-Length: " as *u8); o = d_putn(h, o, served)
853 o = d_puts(h, o, "\r\nAccess-Control-Allow-Origin: *\r\nCache-Control: no-store\r\nConnection: close\r\n\r\n" as *u8)
854 sys_write(cfd, h, o)
855 let partp: *u8 = d_path(dir, "/download.part" as *u8, sys_mmap(640))
856 let pfd: i64 = sys_openat_rd(partp)
857 if pfd >= 0 {
858 sys_lseek(pfd, voff + rs, 0)
859 let chunk: *u8 = sys_mmap(TORRENT_MAGIC_1048576); var rem: i64 = served
860 while rem > 0 { var want: i64 = TORRENT_MAGIC_1048576; if rem < want { want = rem } let g: i64 = sys_read(pfd, chunk, want); if g <= 0 { rem = 0 } else { sys_write(cfd, chunk, g); rem = rem - g } }
861 sys_close(pfd)
862 }
863 sys_close(cfd)
864 return 0
865}
866
867// DEDUP BY INFO-HASH: scan existing torrents' download.magnet for one whose btih == `btih`. Returns 1
868// + copies that torrent's id into out_id (so an add of the same content under a different display name
869// RESUMES the one download instead of creating a duplicate subdir). Works for torrents added before this
870// fix too (it reads their magnet, not a sidecar).
871func d_find_by_btih(outdir: *u8, idxpath: *u8, btih: *u8, btn: i64, out_id: *u8, ocap: i64) -> i64 {
872 let idx: *u8 = sys_mmap(TORRENT_MAGIC_65536); let n: i64 = d_read_file(idxpath, idx, TORRENT_MAGIC_65536)
873 if n <= 0 { return 0 }
874 var i: i64 = 0; var guard: i64 = 0
875 while i < n {
876 if guard > TORRENT_MAGIC_2000 { i = n } else {
877 guard = guard + 1
878 let e: i64 = d_scan_to(idx, n, i, 10)
879 let idlen: i64 = e - i
880 if idlen > 0 { if idlen < 500 {
881 let id: *u8 = sys_mmap(512); var k: i64 = 0; while k < idlen { id[k] = idx[i+k]; k = k + 1 } id[idlen] = 0 as u8
882 let dir: *u8 = d_subdir(outdir, id, sys_mmap(640))
883 let magp: *u8 = d_path(dir, "/download.magnet" as *u8, sys_mmap(640))
884 let mb: *u8 = sys_mmap(TORRENT_MAGIC_4096); let mln: i64 = d_read_file(magp, mb, TORRENT_MAGIC_4095)
885 if mln > 0 {
886 mb[mln] = 0 as u8
887 let eb: *u8 = sys_mmap(128); let ebn: i64 = d_extract_btih(mb, mln, eb, 128)
888 if ebn == btn {
889 var m: i64 = 1; var j: i64 = 0
890 while j < btn { if eb[j] != btih[j] { m = 0; j = btn } else { j = j + 1 } }
891 if m == 1 { var c: i64 = 0; while c < idlen { if c < ocap-1 { out_id[c] = id[c] } c = c + 1 } out_id[idlen] = 0 as u8; return 1 }
892 }
893 }
894 } }
895 i = e + 1
896 }
897 }
898 return 0
899}
900
901// STARTUP AUTO-RESUME (crash/reboot recovery): resume every torrent that is control=active AND has
902// on-disk progress AND whose status file is STALE (not touched in 60s = its worker is dead). The
903// staleness guard is load-bearing: a GRACEFUL daemon restart leaves the workers alive (they double-fork
904// + reparent to init, so they survive), and their status keeps updating -> NOT stale -> NOT re-spawned
905// (no duplicate worker). Only a crash/reboot (workers gone, status frozen) triggers a resume. Returns count.
906func d_autoresume(outdir: *u8, idxpath: *u8) -> i64 {
907 let idx: *u8 = sys_mmap(TORRENT_MAGIC_65536); let n: i64 = d_read_file(idxpath, idx, TORRENT_MAGIC_65536)
908 if n <= 0 { return 0 }
909 let ts: *i64 = sys_mmap(16) as *i64; sys_clock_gettime_real(ts); let now: i64 = ts[0]
910 let stbuf: *u8 = sys_mmap(160)
911 var i: i64 = 0; var guard: i64 = 0; var resumed: i64 = 0
912 while i < n {
913 if guard > TORRENT_MAGIC_2000 { i = n } else {
914 guard = guard + 1
915 let e: i64 = d_scan_to(idx, n, i, 10)
916 let idlen: i64 = e - i
917 if idlen > 0 { if idlen < 500 {
918 let id: *u8 = sys_mmap(512); var k: i64 = 0; while k < idlen { id[k] = idx[i+k]; k = k + 1 } id[idlen] = 0 as u8
919 let dir: *u8 = d_subdir(outdir, id, sys_mmap(640))
920 let ctlp: *u8 = d_path(dir, "/download.control" as *u8, sys_mmap(640)); let cb: *u8 = sys_mmap(16); let cn: i64 = d_read_file(ctlp, cb, 15)
921 var active: i64 = 0; if cn > 0 { if (cb[0] as i64) == 97 { active = 1 } } // 'a'ctive
922 if active == 1 {
923 let hn: *i64 = sys_mmap(16) as *i64; d_have_npc(dir, hn)
924 // RESUMABLE = on-disk progress OR metadata-not-yet-resolved (npc==0). A magnet still fetching
925 // its info dict legitimately has NO progress yet -- the old `d_has_progress` gate skipped it
926 // FOREVER, so a magnet that failed metadata once was never retried (the BANK-155/MGFX-142
927 // "never resolved metadata" stuck bug). npc>0 && have>=npc = COMPLETE -> skip (no forever-respawn).
928 // RESUME ANY active torrent -- the `notdone` gate below is the sole COMPLETE-skip. The old gate
929 // (resumable only if d_has_progress OR npc==0) STRANDED FOREVER the torrents that resolved
930 // metadata (npc>0) but downloaded ZERO pieces (download.done absent -> d_has_progress=0): exactly
931 // BANK-155/ajvr-256/ajvr-166 (the "IGNORING" torrents). They got ONE worker at /add, and once it
932 // died without completing a piece they were NEVER re-spawned -> stuck at have=0 permanently. That
933 // was the REAL "IGNORING" root cause -- not the unchoke timeout, not reciprocity: they simply had
934 // no worker. notdone (npc==0 OR have<npc) already excludes COMPLETE, so resume-all is correct.
935 var resumable: i64 = 1
936 if resumable == 1 {
937 var notdone: i64 = 1; if hn[1] > 0 { if hn[0] >= hn[1] { notdone = 0 } }
938 if notdone == 1 {
939 let statp: *u8 = d_path(dir, "/download.status" as *u8, sys_mmap(640))
940 var stale: i64 = 1
941 if sys_fstatat(statp, stbuf) == 0 { let mtp: *i64 = (((stbuf as i64) + 88)) as *i64; if now - mtp[0] < 60 { stale = 0 } }
942 if stale == 1 {
943 let mp: *u8 = d_path(dir, "/download.magnet" as *u8, sys_mmap(640)); let mb: *u8 = sys_mmap(TORRENT_MAGIC_4096); let mln: i64 = d_read_file(mp, mb, TORRENT_MAGIC_4095)
944 if mln > 0 { mb[mln] = 0 as u8; d_spawn_worker(mb, dir, 1); resumed = resumed + 1 }
945 }
946 }
947 }
948 }
949 } }
950 i = e + 1
951 }
952 }
953 return resumed
954}
955
956// ---- AREA routing + access enforcement (R2: two areas; R3 wires the real session->level) ----
957// scan buf[0..n) for the NUL-terminated literal pat; return 1 if present.
958func d_buf_find(buf: *u8, n: i64, pat: *u8) -> i64 {
959 let pln: i64 = d_strlen(pat)
960 if pln <= 0 { return 0 }
961 var i: i64 = 0
962 while i + pln <= n {
963 var j: i64 = 0; var m: i64 = 1
964 while j < pln { if buf[i+j] != pat[j] { m = 0; j = pln } else { j = j + 1 } }
965 if m == 1 { return 1 }
966 i = i + 1
967 }
968 return 0
969}
970// which destination AREA does this request select? "area=gallery"/"area=nsfw" -> NSFW; default SFW ("media").
971// Default is the LOWER-privilege area so an unspecified/garbled flag can NEVER silently land in NSFW.
972func d_parse_area(req: *u8, n: i64) -> i64 {
973 if d_buf_find(req, n, "area=gallery" as *u8) == 1 { return NX_TAREA_NSFW }
974 if d_buf_find(req, n, "area=nsfw" as *u8) == 1 { return NX_TAREA_NSFW }
975 return NX_TAREA_SFW
976}
977// is this file present? (the local-operator marker check)
978func d_marker_present(path: *u8) -> i64 { let fd: i64 = sys_openat_rd(path); if fd < 0 { return 0 } sys_close(fd); return 1 }
979// parse a non-negative int from the TRUSTED internal header "X-Nishi-Level:" (set by the front gateway). -1 if absent.
980func d_trusted_level(req: *u8, n: i64) -> i64 {
981 let pat: *u8 = "X-Nishi-Level:" as *u8; let pl: i64 = 14
982 var i: i64 = 0; var pos: i64 = 0 - 1
983 while i + pl <= n { var j: i64 = 0; var m: i64 = 1; while j < pl { if req[i+j] != pat[j] { m = 0; j = pl } else { j = j + 1 } } if m == 1 { pos = i + pl; i = n } else { i = i + 1 } }
984 if pos < 0 { return 0 - 1 }
985 if pos < n { if req[pos]==(32 as u8) { pos = pos + 1 } }
986 var v: i64 = 0; var got: i64 = 0
987 while pos < n { let c: i64 = req[pos] as i64; if c >= 48 { if c <= 57 { v = v*10 + (c-48); got = 1; pos = pos + 1 } else { pos = n } } else { pos = n } }
988 if got == 0 { return 0 - 1 }
989 return v
990}
991// the requester's GRANTED level (R3 -- DENY-BY-DEFAULT).
992// LOCAL mode (marker present = the operator's own box): OWNER -- today's single-operator behavior.
993// HUB mode (no marker): the front gateway already authenticated (OPAQUE) + resolved the level via
994// nx_torrent_authn and injected the trusted internal header X-Nishi-Level. Read it; absent/garbled -> 0
995// (ANON), so nx_taccess_allow denies every gated area. The daemon MUST bind localhost behind the gateway in
996// hub mode so an outside client can't spoof the header (Cardinal 12: trust internal, validate at the boundary).
997func d_viewer_level(req: *u8, n: i64) -> i64 {
998 if d_marker_present(TORRENT_LOCAL_MARKER) == 1 { return NX_TACCESS_OWNER }
999 let lv: i64 = d_trusted_level(req, n)
1000 if lv < 0 { return 0 }
1001 return lv
1002}
1003// resolve the dir for torrent `id` in a root the viewer MAY access. SFW always; NSFW only for OWNER.
1004// returns 1 + fills out_dir + out_area[0]=area, else 0 (fail-closed: a family viewer cannot even ADDRESS
1005// an NSFW torrent, so /stream and the control verbs can't reach it by id-guessing).
1006func d_resolve_dir(id: *u8, level: i64, sfw_root: *u8, sfw_idx: *u8, nsfw_root: *u8, nsfw_idx: *u8, out_dir: *u8, out_area: *i64) -> i64 {
1007 let ib: *u8 = sys_mmap(TORRENT_MAGIC_65536); let n1: i64 = d_read_file(sfw_idx, ib, TORRENT_MAGIC_65536)
1008 if n1 > 0 { if d_idx_has(ib, n1, id) == 1 { d_subdir(sfw_root, id, out_dir); out_area[0] = NX_TAREA_SFW; return 1 } }
1009 if level >= NX_TACCESS_OWNER {
1010 let ib2: *u8 = sys_mmap(TORRENT_MAGIC_65536); let n2: i64 = d_read_file(nsfw_idx, ib2, TORRENT_MAGIC_65536)
1011 if n2 > 0 { if d_idx_has(ib2, n2, id) == 1 { d_subdir(nsfw_root, id, out_dir); out_area[0] = NX_TAREA_NSFW; return 1 } }
1012 }
1013 return 0
1014}
1015// append all torrents in ONE root's registry to out (sharing offset + first-flag across roots); return new o.
1016func d_status_root(out: *u8, o0: i64, first_p: *i64, root: *u8, idxpath: *u8) -> i64 {
1017 var o: i64 = o0
1018 let idx: *u8 = sys_mmap(TORRENT_MAGIC_65536); let n: i64 = d_read_file(idxpath, idx, TORRENT_MAGIC_65536)
1019 if n > 0 {
1020 var i: i64 = 0; var guard: i64 = 0
1021 while i < n {
1022 if guard > TORRENT_MAGIC_2000 { i = n } else {
1023 guard = guard + 1
1024 let e: i64 = d_scan_to(idx, n, i, 10)
1025 let len: i64 = e - i
1026 if len > 0 {
1027 let id: *u8 = sys_mmap(128); var k: i64 = 0; while k < len { if k < 127 { id[k] = idx[i+k]; k = k + 1 } else { k = len } } id[len] = 0 as u8
1028 if d_idx_has(idx, i, id) == 0 {
1029 let dir: *u8 = d_subdir(root, id, sys_mmap(640))
1030 if first_p[0] == 0 { o = d_puts(out, o, "," as *u8) } first_p[0] = 0
1031 o = d_status_one(out, o, id, dir)
1032 }
1033 }
1034 i = e + 1
1035 }
1036 }
1037 }
1038 return o
1039}
1040// GET /status: aggregate the torrents the viewer MAY see -- SFW always, NSFW only for OWNER. Isolation by
1041// construction: a family viewer never even scans the NSFW root, so an NSFW torrent cannot appear in their list.
1042func d_status_two(cfd: i64, level: i64, sfw_root: *u8, sfw_idx: *u8, nsfw_root: *u8, nsfw_idx: *u8) -> i64 {
1043 let out: *u8 = sys_mmap(TORRENT_MAGIC_262144); var o: i64 = 0
1044 o = d_puts(out, o, "{\"downloads\":[" as *u8)
1045 let fp: *i64 = sys_mmap(16) as *i64; fp[0] = 1
1046 o = d_status_root(out, o, fp, sfw_root, sfw_idx)
1047 if level >= NX_TACCESS_OWNER { o = d_status_root(out, o, fp, nsfw_root, nsfw_idx) }
1048 o = d_puts(out, o, "]}" as *u8)
1049 d_send(cfd, "200 OK" as *u8, "application/json" as *u8, out, o)
1050 return 0
1051}
1052
1053// ---- router ------------------------------------------------------
1054func d_route(cfd: i64, req: *u8, method: i64, po: i64, pl: i64, cl_: i64, bo: i64, rn: i64,
1055 sfw_root: *u8, sfw_idx: *u8, nsfw_root: *u8, nsfw_idx: *u8, indexp: *u8, appjsp: *u8, discpath: *u8) -> i64 {
1056 let level: i64 = d_viewer_level(req, rn)
1057 if method == 1 {
1058 if d_streq(req,po,pl,"/torrent" as *u8)==1 { d_serve_file(cfd, indexp, "text/html; charset=utf-8" as *u8); return 0 }
1059 if d_streq(req,po,pl,"/torrent/" as *u8)==1 { d_serve_file(cfd, indexp, "text/html; charset=utf-8" as *u8); return 0 }
1060 if d_streq(req,po,pl,"/" as *u8)==1 { d_serve_file(cfd, indexp, "text/html; charset=utf-8" as *u8); return 0 }
1061 if d_streq(req,po,pl,"/torrent/app.js" as *u8)==1 { d_serve_file(cfd, appjsp, "application/javascript" as *u8); return 0 }
1062 if d_streq(req,po,pl,"/torrent/api/me" as *u8)==1 { d_send_str(cfd, "200 OK" as *u8, "application/json" as *u8, "{\"user\":\"local\"}" as *u8); return 0 }
1063 if d_streq(req,po,pl,"/torrent/api/status" as *u8)==1 { d_status_two(cfd, level, sfw_root, sfw_idx, nsfw_root, nsfw_idx); return 0 }
1064 // STREAM the video file's downloaded prefix (HTTP Range) so a <video> can play while/after download.
1065 if d_starts(req,po,pl,"/torrent/stream/" as *u8)==1 {
1066 var idlen: i64 = pl - 16 // strip "/torrent/stream/" (16)
1067 if idlen > 511 { idlen = 511 }
1068 if idlen > 0 {
1069 let id: *u8 = sys_mmap(512); var k: i64 = 0; while k < idlen { id[k] = req[po+16+k]; k = k + 1 } id[idlen] = 0 as u8
1070 let dir: *u8 = sys_mmap(640); let sa: *i64 = sys_mmap(16) as *i64
1071 if d_resolve_dir(id, level, sfw_root, sfw_idx, nsfw_root, nsfw_idx, dir, sa) == 1 {
1072 d_stream(cfd, req, rn, dir)
1073 } else { d_send_str(cfd, "404 Not Found" as *u8, "text/plain" as *u8, "not found\n" as *u8) }
1074 } else { d_send_str(cfd, "400 Bad Request" as *u8, "text/plain" as *u8, "no id\n" as *u8) }
1075 return 0
1076 }
1077 if d_streq(req,po,pl,"/torrent/api/discover_result" as *u8)==1 { d_discover_result(cfd, discpath); return 0 }
1078 d_send_str(cfd, "404 Not Found" as *u8, "text/plain" as *u8, "not found\n" as *u8); return 0
1079 }
1080 if method == 2 {
1081 if d_streq(req,po,pl,"/torrent/api/login" as *u8)==1 { d_send_str(cfd, "200 OK" as *u8, "application/json" as *u8, "{\"ok\":true}" as *u8); return 0 }
1082 if d_streq(req,po,pl,"/torrent/api/add" as *u8)==1 {
1083 // The POST body can trail the headers in a later TCP segment, and some clients
1084 // omit a parseable Content-Length. So: drain the socket (idle-bounded) into the
1085 // same buffer, then search the WHOLE request -- the "magnet:" run never occurs
1086 // in HTTP headers, so a full-buffer scan is unambiguous and Content-Length-free.
1087 sys_set_socket_timeout(cfd, 2)
1088 var tot: i64 = rn
1089 var go: i64 = 1
1090 while go == 1 {
1091 if tot >= TORRENT_MAGIC_16384 { go = 0 } else {
1092 let r: i64 = sys_read(cfd, req + tot, TORRENT_MAGIC_16384 - tot)
1093 if r <= 0 { go = 0 } else { tot = tot + r }
1094 }
1095 }
1096 let mag: *u8 = sys_mmap(TORRENT_MAGIC_4096)
1097 var mn: i64 = d_form_magnet(((req as i64) + bo) as *u8, tot - bo, mag, TORRENT_MAGIC_4096) // browser UI: urlencoded magnet=<%XX> -> decode
1098 if mn <= 0 { mn = d_extract_magnet(req, tot, mag, TORRENT_MAGIC_4096) } // JSON/raw fallback (the gate path)
1099 if mn <= 0 { d_send_str(cfd, "400 Bad Request" as *u8, "application/json" as *u8, "{\"error\":\"no magnet\"}" as *u8); return 0 }
1100 // AREA + ACCESS (R2): which destination did the request flag, and may THIS viewer reach it?
1101 // Deny-by-default via nx_taccess_allow (SFW="media" needs FAMILY level; NSFW="gallery" needs OWNER).
1102 let area: i64 = d_parse_area(req, tot)
1103 if nx_taccess_allow(level, area) != NX_TACCESS_ALLOW {
1104 d_send_str(cfd, "403 Forbidden" as *u8, "application/json" as *u8, "{\"error\":\"access denied for this area\"}" as *u8); return 0
1105 }
1106 var aroot: *u8 = sfw_root; var aidx: *u8 = sfw_idx
1107 if area == NX_TAREA_NSFW { aroot = nsfw_root; aidx = nsfw_idx }
1108 // MULTI-TORRENT: id = the magnet's btih; each torrent gets its OWN <area-root>/<id> + a registry
1109 // entry, so concurrent downloads never clobber each other (the BBB-clobber bug).
1110 let hbv: *u8 = sys_mmap(128); let hbn: i64 = d_extract_btih(mag, mn, hbv, 128); if hbn <= 0 { d_send_str(cfd, "400 Bad Request" as *u8, "application/json" as *u8, "{\"error\":\"bad magnet (no btih)\"}" as *u8); return 0 }
1111 // DEDUP BY INFO-HASH (within the area): if this exact content is already here (any display name),
1112 // RESUME that one download instead of spawning a duplicate subdir (the 326FCT-218-added-twice bug).
1113 let exid: *u8 = sys_mmap(512)
1114 if d_find_by_btih(aroot, aidx, hbv, hbn, exid, 512) == 1 {
1115 let edir: *u8 = d_subdir(aroot, exid, sys_mmap(640))
1116 d_write_file(d_path(edir, "/download.control" as *u8, sys_mmap(640)), "active" as *u8, 6)
1117 d_spawn_worker(mag, edir, 1)
1118 let rb0: *u8 = sys_mmap(256); var r0: i64 = d_puts(rb0, 0, "{\"id\":\"" as *u8); r0 = d_puts(rb0, r0, exid); r0 = d_puts(rb0, r0, "\",\"dedup\":true}" as *u8); rb0[r0] = 0 as u8
1119 d_send_str(cfd, "200 OK" as *u8, "application/json" as *u8, rb0); return 0
1120 }
1121 // folder/id = the TITLE (dn=), sanitized -- so Downloads shows "javdb.com_326FCT-218/" not a raw
1122 // hash. Falls back to the info-hash only when the magnet carries no dn=.
1123 let id: *u8 = sys_mmap(512); var idl: i64 = d_extract_name(mag, mn, id, 512)
1124 if idl <= 0 { idl = d_extract_btih(mag, mn, id, 512) }
1125 let dir: *u8 = d_subdir(aroot, id, sys_mmap(640))
1126 sys_mkdir(dir, 0x1ff)
1127 d_write_file(d_path(dir, "/download.magnet" as *u8, sys_mmap(640)), mag, mn)
1128 d_write_file(d_path(dir, "/download.control" as *u8, sys_mmap(640)), "active" as *u8, 6)
1129 d_idx_add(aidx, id)
1130 d_spawn_worker(mag, dir, d_has_progress(dir)) // RESUME if a partial download already exists (idempotent re-add)
1131 let rb: *u8 = sys_mmap(256); var ro: i64 = d_puts(rb, 0, "{\"id\":\"" as *u8); ro = d_puts(rb, ro, id); ro = d_puts(rb, ro, "\"}" as *u8); rb[ro] = 0 as u8
1132 d_send_str(cfd, "200 OK" as *u8, "application/json" as *u8, rb); return 0
1133 }
1134 if d_streq(req,po,pl,"/torrent/api/discover" as *u8)==1 {
1135 sys_set_socket_timeout(cfd, 2)
1136 var tot: i64 = rn
1137 var go: i64 = 1
1138 while go == 1 {
1139 if tot >= TORRENT_MAGIC_16384 { go = 0 } else {
1140 let r: i64 = sys_read(cfd, req + tot, TORRENT_MAGIC_16384 - tot)
1141 if r <= 0 { go = 0 } else { tot = tot + r }
1142 }
1143 }
1144 let urlb: *u8 = sys_mmap(TORRENT_MAGIC_4096)
1145 let un: i64 = d_extract_url(req + bo, tot - bo, urlb, TORRENT_MAGIC_4096)
1146 if un <= 0 { d_send_str(cfd, "400 Bad Request" as *u8, "application/json" as *u8, "{\"error\":\"no url\"}" as *u8); return 0 }
1147 d_spawn_discover(urlb, discpath)
1148 d_send_str(cfd, "200 OK" as *u8, "application/json" as *u8, "{\"ok\":true}" as *u8); return 0
1149 }
1150 if d_streq(req,po,pl,"/torrent/api/video" as *u8)==1 {
1151 sys_set_socket_timeout(cfd, 2)
1152 var tot: i64 = rn; var go: i64 = 1
1153 while go == 1 { if tot >= TORRENT_MAGIC_16384 { go = 0 } else { let r: i64 = sys_read(cfd, req + tot, TORRENT_MAGIC_16384 - tot); if r <= 0 { go = 0 } else { tot = tot + r } } }
1154 let urlb: *u8 = sys_mmap(TORRENT_MAGIC_4096); let un: i64 = d_extract_url(req + bo, tot - bo, urlb, TORRENT_MAGIC_4096)
1155 if un <= 0 { d_send_str(cfd, "400 Bad Request" as *u8, "application/json" as *u8, "{\"error\":\"no url\"}" as *u8); return 0 }
1156 let varea: i64 = d_parse_area(req, tot)
1157 if nx_taccess_allow(level, varea) != NX_TACCESS_ALLOW { d_send_str(cfd, "403 Forbidden" as *u8, "application/json" as *u8, "{\"error\":\"access denied for this area\"}" as *u8); return 0 }
1158 var vroot: *u8 = sfw_root; if varea == NX_TAREA_NSFW { vroot = nsfw_root }
1159 let vdir: *u8 = d_path(vroot, "/videos" as *u8, sys_mmap(640)); sys_mkdir(vdir, 0x1ff)
1160 let vref: *u8 = d_url_referer(urlb, sys_mmap(TORRENT_MAGIC_4200)) // same-origin Referer so CDNs that gate direct links serve the bytes (empty = #1 cause of 403)
1161 if d_is_hls(urlb) == 1 { // HLS .m3u8 -> fetch+stitch (+AES decrypt) all segments via nx_hls_get
1162 let houtp: *u8 = d_hls_outname(urlb, vdir, sys_mmap(TORRENT_MAGIC_1024))
1163 d_spawn_hls(urlb, houtp, vref)
1164 } else {
1165 let outp: *u8 = d_vidname(urlb, vdir, sys_mmap(TORRENT_MAGIC_1024))
1166 d_spawn_video(urlb, outp, vref)
1167 }
1168 d_send_str(cfd, "200 OK" as *u8, "application/json" as *u8, "{\"ok\":true}" as *u8); return 0
1169 }
1170 // MULTI-TORRENT control: /torrent/api/<id>/{pause,resume,remove}, id = the torrent's btih.
1171 if d_starts(req,po,pl,"/torrent/api/" as *u8)==1 {
1172 var act: i64 = 0; var sl: i64 = 0
1173 if d_ends(req,po,pl,"/pause" as *u8)==1 { act = 1; sl = 6 }
1174 if d_ends(req,po,pl,"/resume" as *u8)==1 { act = 2; sl = 7 }
1175 if d_ends(req,po,pl,"/remove" as *u8)==1 { act = 3; sl = 7 }
1176 if d_ends(req,po,pl,"/reannounce" as *u8)==1 { act = 4; sl = 11 } // the UNSTICK lever
1177 if d_ends(req,po,pl,"/recheck" as *u8)==1 { act = 5; sl = 8 }
1178 if act > 0 {
1179 var idlen: i64 = pl - 13 - sl // strip "/torrent/api/" (13) + the action suffix
1180 if idlen > 127 { idlen = 127 } // defensive: id buffer is 128
1181 if idlen > 0 {
1182 let id: *u8 = sys_mmap(128); var k: i64 = 0; while k < idlen { if k < 127 { id[k] = req[po+13+k]; k = k + 1 } else { k = idlen } } id[idlen] = 0 as u8
1183 let dir: *u8 = sys_mmap(640); let ca: *i64 = sys_mmap(16) as *i64
1184 if d_resolve_dir(id, level, sfw_root, sfw_idx, nsfw_root, nsfw_idx, dir, ca) == 1 {
1185 if act == 1 { d_write_file(d_path(dir, "/download.control" as *u8, sys_mmap(640)), "paused" as *u8, 6) }
1186 if act == 2 {
1187 d_write_file(d_path(dir, "/download.control" as *u8, sys_mmap(640)), "active" as *u8, 6)
1188 let mp: *u8 = d_path(dir, "/download.magnet" as *u8, sys_mmap(640)); let mb2: *u8 = sys_mmap(TORRENT_MAGIC_4096); let mln: i64 = d_read_file(mp, mb2, TORRENT_MAGIC_4095)
1189 if mln > 0 { mb2[mln] = 0 as u8; d_spawn_worker(mb2, dir, 1) }
1190 }
1191 if act == 3 {
1192 d_write_file(d_path(dir, "/download.control" as *u8, sys_mmap(640)), "removed" as *u8, 7)
1193 if ca[0] == NX_TAREA_NSFW { d_idx_remove(nsfw_idx, id) } else { d_idx_remove(sfw_idx, id) }
1194 }
1195 if act == 4 { // force-reannounce: respawn the worker -> re-hits trackers/DHT + re-discovers peers (the UNSTICK for IGNORING torrents)
1196 d_write_file(d_path(dir, "/download.control" as *u8, sys_mmap(640)), "active" as *u8, 6)
1197 let mp4: *u8 = d_path(dir, "/download.magnet" as *u8, sys_mmap(640)); let mb4: *u8 = sys_mmap(TORRENT_MAGIC_4096); let ml4: i64 = d_read_file(mp4, mb4, TORRENT_MAGIC_4095)
1198 if ml4 > 0 { mb4[ml4] = 0 as u8; d_spawn_worker(mb4, dir, 1) }
1199 }
1200 if act == 5 { // force-recheck: signal the worker to re-verify on-disk pieces
1201 d_write_file(d_path(dir, "/download.control" as *u8, sys_mmap(640)), "recheck" as *u8, 7)
1202 }
1203 }
1204 }
1205 }
1206 d_send_str(cfd, "200 OK" as *u8, "application/json" as *u8, "{\"ok\":true}" as *u8); return 0
1207 }
1208 d_send_str(cfd, "404 Not Found" as *u8, "text/plain" as *u8, "not found\n" as *u8); return 0
1209 }
1210 d_send_str(cfd, "405 Method Not Allowed" as *u8, "text/plain" as *u8, "method\n" as *u8)
1211 return 0
1212}
1213
1214func main(argc: i64, argv: *i64) -> i64 {
1215 // TWO download AREAS (operator 2026-06-20): a lower-privilege "media" area (family+) and an owner-only area.
1216 // Each area = its OWN root + registry, so a member's /status never even scans the owner root (isolation by
1217 // construction). DATA-DRIVEN ROOTS (CLAUDE.md #17): argv overrides the workstation defaults so the SAME binary
1218 // runs NAS-native -- argv[1]=media/SFW root (e.g. /volume1/movies), argv[2]=owner/NSFW root (e.g. /volume1/logging),
1219 // argv[3]=the UI shell file. idx/discover are derived from the roots. The worker stays /tmp (deployed at launch).
1220 var sfw_root: *u8 = "/mnt/c/Users/elder/Downloads/nishi-media" as *u8
1221 var nsfw_root: *u8 = "/mnt/c/Users/elder/Downloads/nishi-torrents" as *u8
1222 var indexp: *u8 = "/mnt/c/Users/elder/nishi-core/nxc2/sites/nishifamily/torrent/index.html" as *u8
1223 if argc > 1 { sfw_root = argv[1] as *u8 }
1224 if argc > 2 { nsfw_root = argv[2] as *u8 }
1225 if argc > 3 { indexp = argv[3] as *u8 }
1226 let sfw_idx: *u8 = d_path(sfw_root, "/torrents.idx" as *u8, sys_mmap(700))
1227 let nsfw_idx: *u8 = d_path(nsfw_root, "/torrents.idx" as *u8, sys_mmap(700))
1228 let discpath: *u8 = d_path(nsfw_root, "/discover.tsv" as *u8, sys_mmap(700))
1229 let appjsp: *u8 = indexp // the emitted shell is self-contained; /torrent/app.js serves it too (harmless)
1230 sys_mkdir(sfw_root, 0x1ff); sys_mkdir(nsfw_root, 0x1ff) // ensure both areas exist
1231
1232 let addr: *u8 = sys_mmap(16)
1233 nx_http_server_addr_any(addr, TORRENT_MAGIC_8097) // 0.0.0.0:TORRENT_MAGIC_8097 base; tightened to loopback below unless bind_mode="any"
1234 // SECURITY (s-class): behind the OPAQUE gateway the daemon TRUSTS the injected X-Nishi-Level header, so a
1235 // LAN-direct client reaching 0.0.0.0:8097 could FORGE that header and bypass auth. Default + the NAS "hub"
1236 // profile therefore bind LOOPBACK (only the loopback gateway on :18793 reaches :8097). Only an explicit
1237 // bind_mode="any" (argv[4]) keeps 0.0.0.0 -- the dev laptop needs it for WSL2 localhost-forwarding.
1238 var bind_any: i64 = 0
1239 if argc > 4 { if d_streq(argv[4] as *u8, 0, 3, "any" as *u8) == 1 { bind_any = 1 } }
1240 if bind_any == 0 {
1241 addr[4] = 127 as u8; addr[5] = 0 as u8; addr[6] = 0 as u8; addr[7] = 1 as u8
1242 sys_write(1, "TORRENTD bind=LOOPBACK 127.0.0.1:8097 (gateway-only)\n" as *u8, 53)
1243 } else {
1244 sys_write(1, "TORRENTD bind=ANY 0.0.0.0:8097 (dev/WSL2)\n" as *u8, 42)
1245 }
1246 let vp: *i64 = sys_mmap(16) as *i64
1247 let lfd: i64 = nx_http_server_listen(addr, 16, vp)
1248 if lfd < 0 {
1249 sys_write(1, "TORRENTD bind FAILED (port 8097 in use?)\n" as *u8, 41); sys_exit(1); return 1
1250 }
1251 // FD_CLOEXEC on the listen socket: the download WORKERS are fork+execve'd from request-handling
1252 // children and would otherwise INHERIT this :8097 fd, holding the port alive after the daemon dies
1253 // and blocking every restart (the bug that wedged the redeploy). CLOEXEC closes it across execve.
1254 __syscall(72, lfd, 2, 1, 0, 0, 0) // fcntl(lfd, F_SETFD=2, FD_CLOEXEC=1) -> x86_64 nr 72
1255 sys_write(1, "TORRENTD listening http://127.0.0.1:8097/torrent\n" as *u8, 48)
1256 let rsm: i64 = d_autoresume(nsfw_root, nsfw_idx) + d_autoresume(sfw_root, sfw_idx) // crash/reboot recovery: resume active downloads in BOTH areas whose worker died
1257 let rb1: *u8 = sys_mmap(64); var ro1: i64 = d_puts(rb1, 0, "TORRENTD autoresume resumed=" as *u8); ro1 = d_putn(rb1, ro1, rsm); rb1[ro1] = 10 as u8; sys_write(1, rb1, ro1 + 1)
1258
1259 // ---- PERSISTENT RETRY SWEEP (rung #2: the poorly-seeded "stuck at 99%" fix) ----------------------
1260 // A detached child re-runs the resume sweep every D_RETRY_INTERVAL_MS so a torrent whose worker gave
1261 // up (~20min of no new pieces) keeps getting re-spawned -> re-announces (re-triggering the wide
1262 // tracker scan, rung #1) -> it finishes whenever a seed with the missing tail piece appears. The sweep
1263 // REUSES d_autoresume, which already skips COMPLETE + paused + live-worker torrents, so only genuinely
1264 // stuck ones are re-spawned. SINGLE-INSTANCE via a generation file (.sweeper.gen): on a daemon restart
1265 // the new sweeper claims a fresh gen, so the old (reparented-to-init) one retires on its next wake ->
1266 // no sweeper pile-up, no tracker-ban spam. fds 3..255 closed so it never holds the :8097 listen socket.
1267 let genp: *u8 = d_path(nsfw_root, "/.sweeper.gen" as *u8, sys_mmap(700))
1268 let swp1: i64 = sys_fork()
1269 if swp1 == 0 {
1270 let swp2: i64 = sys_fork()
1271 if swp2 == 0 {
1272 var sfdx: i64 = 3; while sfdx < 256 { sys_close(sfdx); sfdx = sfdx + 1 }
1273 let gts: *i64 = sys_mmap(16) as *i64; sys_clock_gettime_real(gts); let mygen: i64 = gts[0]*TORRENT_MAGIC_1000000000 + gts[1]
1274 let gbuf: *u8 = sys_mmap(32); let gl: i64 = d_putn(gbuf, 0, mygen); d_write_file(genp, gbuf, gl)
1275 var run: i64 = 1
1276 while run == 1 {
1277 sys_sleep_ms(D_RETRY_INTERVAL_MS)
1278 let rbuf: *u8 = sys_mmap(32); let rgn: i64 = d_read_file(genp, rbuf, 31)
1279 var cur: i64 = 0; if rgn > 0 { cur = d_int_at(rbuf, rgn, 0) }
1280 if cur != mygen { run = 0 } else {
1281 let got: i64 = d_autoresume(nsfw_root, nsfw_idx) + d_autoresume(sfw_root, sfw_idx)
1282 let lb: *u8 = sys_mmap(64); var lo: i64 = d_puts(lb, 0, "TORRENTD retry-sweep re-spawned=" as *u8); lo = d_putn(lb, lo, got); lb[lo] = 10 as u8; sys_write(1, lb, lo + 1)
1283 }
1284 }
1285 sys_exit(0)
1286 }
1287 sys_exit(0)
1288 }
1289 let swst: *i64 = sys_mmap(16) as *i64; sys_wait4(swp1, swst, 0)
1290
1291 let req: *u8 = sys_mmap(TORRENT_MAGIC_16384)
1292 let m: *i64 = sys_mmap(16) as *i64; let po: *i64 = sys_mmap(16) as *i64; let pl: *i64 = sys_mmap(16) as *i64
1293 let cl_: *i64= sys_mmap(16) as *i64; let bo: *i64 = sys_mmap(16) as *i64; let rn: *i64 = sys_mmap(16) as *i64
1294
1295 // fork-per-connection: a browser opens several sockets (page, app.js, /me, then
1296 // /status every second). A single blocking accept-loop would stall if any one client
1297 // is slow/half-open. Each connection is handled in a disposable child; the parent
1298 // just accepts + reaps. Children are bounded by a socket read timeout (no-hang law).
1299 let st: *i64 = sys_mmap(16) as *i64
1300 var serving: i64 = 1
1301 while serving == 1 {
1302 let cfd: i64 = nx_http_server_accept_one(lfd, vp)
1303 if cfd >= 0 {
1304 let pid: i64 = sys_fork()
1305 if pid == 0 {
1306 sys_set_socket_timeout(cfd, 10)
1307 let rc: i64 = nx_http_server_read_request(cfd, req, TORRENT_MAGIC_16384, m, po, pl, cl_, bo, rn)
1308 if rc == 0 {
1309 d_route(cfd, req, m[0], po[0], pl[0], cl_[0], bo[0], rn[0], sfw_root, sfw_idx, nsfw_root, nsfw_idx, indexp, appjsp, discpath)
1310 } else {
1311 d_send_str(cfd, "400 Bad Request" as *u8, "text/plain" as *u8, "bad request\n" as *u8)
1312 }
1313 sys_exit(0)
1314 }
1315 sys_close(cfd)
1316 var reap: i64 = 1
1317 while reap == 1 { let w: i64 = sys_wait4(0 - 1, st, WNOHANG); if w <= 0 { reap = 0 } }
1318 }
1319 }
1320 return 0
1321}