code wiki / (root) / nx_torrent_daemon.nx

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}