nx_archive_server.nx source
↩ module page · 311 lines · 22935 B
1// nx_archive_server.nx -- sovereign localhost HTTP server for the time machine (R5 serve side). Binds 0.0.0.0:18801 (ARCSRV_PORT -- the bind DERIVES from that const, see below),
2// serves files under web_assets/ (path-traversal rejected), Content-Type by extension. "/" -> a landing page. Also
3// hosts the SUPERVISED do-not-recover blocklist: GET /blocklist renders it live from seg_store (justification +
4// per-entry action), GET /blocklist/approve?id=N and /blocklist/retract?id=N are the operator ACTIONS (flip status on
5// seg_store, then 303 back to /blocklist). Pending entries never block until approved here. Fetch stack. license_tier: ORIGINAL
6import "nx_syscalls.nx"
7import "nx_blocklist_view.nx"
8// *PORT CONFLICT AVERTED 2026-08-06: this bound 0.0.0.0:8080, which nx_hostctl status shows is ALREADY
9// HELD by the live redirect.elf. Deploying on 8080 would have taken down a production service, and the
10// never-brick health probe would have seen a listener on 8080 and called it healthy -- the collision would
11// have looked like success. 18795 is unused across the whole live inventory (checked against nx_hostctl
12// status: 8443 sites, 8080 redirect, 18190/18090 gallery, 8791 media, 8097/18793/6881 torrent, 8446 vroom,
13// 18096 tools, 18097 fin, 7702 mp, 18098 mgmt, 18456/18465/18466 portal, 9091 opaque, 18791 wiki, 18797 dev).
14// *RENAMED from K_MAGIC_8080. The old name encoded a value the const no longer had, and worse, nothing
15// read it at all -- see the bind below. A name that lies about its value on a const that nothing reads is
16// two independent falsehoods stacked on one line.
17const ARCSRV_PORT: i64 = 18801
18const K_MAGIC_8192: i64 = 8192
19const K_MAGIC_2048: i64 = 2048
20const K_MAGIC_2097152: i64 = 2097152
21
22func sw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
23func ap(buf: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8) { buf[off+i]=s[i]; i=i+1 } return off+i }
24func apn(buf: *u8, off: i64, v: i64) -> i64 { if v==0 { buf[off]=0x30 as u8; return off+1 } var m: i64=v; let t: *u8=sys_mmap(24); var k: i64=0; while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } var o: i64=off; var q: i64=k-1; while q>=0 { buf[o]=t[q]; o=o+1; q=q-1 } return o }
25func endswith(path: *u8, plen: i64, ext: *u8, elen: i64) -> i64 { if plen<elen { return 0 } var i: i64=0; while i<elen { if (path[plen-elen+i] as i64) != (ext[i] as i64) { return 0 } i=i+1 } return 1 }
26// ---- ENHANCED-VIEW TOGGLE --------------------------------------------------------------------
27// nx_dedither writes a de-dithered render beside each dithered original as <name>.enh.png and NEVER
28// touches the source. That is the right archival choice and it left the enhanced renders invisible:
29// nothing on any page linked to them. *A RESTORATION NOBODY CAN SEE IS INDISTINGUISHABLE FROM ONE THAT
30// WAS NEVER MADE.
31// INJECTED AT SERVE TIME, NOT WRITTEN INTO THE PAGE. The stored 1996 HTML stays byte-exact (it is the
32// record), every archived page gains the toggle at once, and no page needs regenerating. The toggle is
33// OFF by default, so the default view remains what the archive actually captured.
34// FAIL-SOFT BY CONSTRUCTION: only a minority of assets are dithered, so most have no .enh.png at all.
35// The onerror handler restores the original src, meaning a missing enhanced render degrades to the
36// original image rather than a broken one -- the toggle cannot make the page worse than it was.
37const ARC_TOGGLE: *u8 = "<div style='position:fixed;top:0;right:0;z-index:99999;background:#111;color:#eee;font:13px system-ui;padding:6px 11px;opacity:.92;border-radius:0 0 0 8px'><label><input type=checkbox onchange='nxT(this.checked)'> enhanced</label></div><script>function nxT(on){var a=document.images,i;for(i=0;i<a.length;i++){var m=a[i];if(!m.getAttribute('data-nxo'))m.setAttribute('data-nxo',m.getAttribute('src'));var o=m.getAttribute('data-nxo');if(on){m.onerror=function(){this.onerror=null;this.src=this.getAttribute('data-nxo')};m.src=o+'.enh.png'}else{m.onerror=null;m.src=o}}}</script>" as *u8
38
39// ---- DIRECTORY INDEX ------------------------------------------------------------------------
40// *AN EMPTY 200 IS WORSE THAN A 404. A directory URL used to fall through to the static-file branch,
41// where sys_read_file OPENS a directory successfully and then reads ZERO bytes -- so the server replied
42// `200 OK, Content-Length: 0` and every browser rendered a blank page. The status line claimed the thing
43// was delivered. That is a PARTIAL APPLY WEARING THE SUCCESS WORD, and it cost an operator a bug report
44// that read as "the server is down" when the server was fine and the ANSWER was empty.
45// An archive whose directories cannot be browsed is a filing cabinet welded shut, so the fix is a real
46// listing rather than an honest 404.
47// getdents64 doubles as the type test: it returns -ENOTDIR on a regular file, so one call both DETECTS
48// the directory and READS it -- no stat race between the check and the use.
49const AS_RECLEN_OFF: i64 = 16
50const AS_NAME_OFF: i64 = 19
51const AS_DIRBUF: i64 = 262144
52const AS_DT_DIR: i64 = 4
53
54// Render an HTML index for `path` into out; hrefs are absolute, built from the REQUEST path so the page
55// works whether or not the URL carried a trailing slash. Returns byte length, or -1 if not a directory.
56func as_dirlist(path: *u8, req: *u8, pst: i64, pen: i64, out: *u8) -> i64 {
57 let fd: i64 = sys_openat_rd(path)
58 if fd < 0 { return 0 - 1 }
59 let db: *u8 = sys_mmap(AS_DIRBUF)
60 let n: i64 = sys_getdents64(fd, db, AS_DIRBUF)
61 sys_close(fd)
62 if n <= 0 { return 0 - 1 }
63 var o: i64 = ap(out, 0, "<!DOCTYPE html><meta charset=utf-8><title>Index of " as *u8)
64 var u: i64 = pst
65 while u < pen { out[o] = req[u]; o = o + 1; u = u + 1 }
66 o = ap(out, o, "</title><body style='font-family:system-ui;background:#0e0f13;color:#e7e7ea;max-width:900px;margin:40px auto;padding:0 20px'><h1 style='font-size:1.3rem'>Index of " as *u8)
67 var u2: i64 = pst
68 while u2 < pen { out[o] = req[u2]; o = o + 1; u2 = u2 + 1 }
69 o = ap(out, o, "</h1><ul style='line-height:1.8;list-style:none;padding:0'>" as *u8)
70 var count: i64 = 0
71 var p: i64 = 0
72 while p < n {
73 let reclen: i64 = (db[p+AS_RECLEN_OFF] as i64) | ((db[p+AS_RECLEN_OFF+1] as i64) << 8)
74 if reclen <= 0 { p = n } else {
75 let dtype: i64 = db[p+18] as i64
76 let nm: i64 = p + AS_NAME_OFF
77 // skip "." and ".."; and REFUSE any name carrying HTML metacharacters -- a filename is data
78 // that arrived from the filesystem, which is a boundary (rule 12), not a trusted literal.
79 var skip: i64 = 0
80 if (db[nm] as i64) == 46 { if (db[nm+1] as i64) == 0 { skip = 1 } }
81 if (db[nm] as i64) == 46 { if (db[nm+1] as i64) == 46 { if (db[nm+2] as i64) == 0 { skip = 1 } } }
82 var q: i64 = nm
83 while db[q] != (0 as u8) {
84 let cc: i64 = db[q] as i64
85 if cc == 60 { skip = 1 }
86 if cc == 62 { skip = 1 }
87 if cc == 34 { skip = 1 }
88 q = q + 1
89 }
90 if skip == 0 {
91 o = ap(out, o, "<li><a href='" as *u8)
92 var u3: i64 = pst
93 while u3 < pen { out[o] = req[u3]; o = o + 1; u3 = u3 + 1 }
94 if (req[pen-1] as i64) != 47 { out[o] = 47 as u8; o = o + 1 }
95 var w: i64 = nm
96 while db[w] != (0 as u8) { out[o] = db[w]; o = o + 1; w = w + 1 }
97 o = ap(out, o, "'>" as *u8)
98 var w2: i64 = nm
99 while db[w2] != (0 as u8) { out[o] = db[w2]; o = o + 1; w2 = w2 + 1 }
100 if dtype == AS_DT_DIR { o = ap(out, o, "/" as *u8) }
101 o = ap(out, o, "</a>" as *u8)
102 count = count + 1
103 }
104 p = p + reclen
105 }
106 }
107 o = ap(out, o, "</ul><p style=color:#8a8f9a>" as *u8)
108 o = apn(out, o, count)
109 o = ap(out, o, " entries — served sovereignly.</p></body>" as *u8)
110 return o
111}
112
113// *A TIME MACHINE FOR THE 90s/2000s WEB MUST SPEAK THE 90s/2000s WEB. This previously knew five types and
114// fell through to text/plain, which does not fail loudly -- the browser simply refuses to render, and an
115// archive that HAS the bytes looks like an archive that lost them. Era formats restored below (Rule 25:
116// build intelligence, never strip features).
117// RENDERABLE by a modern browser: gif (incl. animated), jpeg, png, bmp, ico, svg, webp, avif.
118// ERA-CORRECT but NOT natively renderable: tiff, xbm, pcx (served with true type so they download
119// intact and a converter can reach them, rather than being mangled as text).
120// THE REST OF A PERIOD SITE: midi/wav/au/mp3 background audio, swf, RealMedia .rm/.ram, avi/mov/mpeg.
121// These are a real part of what those sites WERE; serving the honest Content-Type preserves them even
122// where no plugin survives to play them.
123func ctype(path: *u8, plen: i64) -> *u8 {
124 if endswith(path,plen,".html" as *u8,5)==1 { return "text/html" as *u8 }
125 if endswith(path,plen,".htm" as *u8,4)==1 { return "text/html" as *u8 }
126 if endswith(path,plen,".css" as *u8,4)==1 { return "text/css" as *u8 }
127 if endswith(path,plen,".js" as *u8,3)==1 { return "application/javascript" as *u8 }
128 if endswith(path,plen,".json" as *u8,5)==1 { return "application/json" as *u8 }
129 // --- images the browser renders
130 if endswith(path,plen,".gif" as *u8,4)==1 { return "image/gif" as *u8 }
131 if endswith(path,plen,".jpg" as *u8,4)==1 { return "image/jpeg" as *u8 }
132 if endswith(path,plen,".jpeg" as *u8,5)==1 { return "image/jpeg" as *u8 }
133 if endswith(path,plen,".jpe" as *u8,4)==1 { return "image/jpeg" as *u8 }
134 if endswith(path,plen,".png" as *u8,4)==1 { return "image/png" as *u8 }
135 if endswith(path,plen,".bmp" as *u8,4)==1 { return "image/bmp" as *u8 }
136 if endswith(path,plen,".ico" as *u8,4)==1 { return "image/x-icon" as *u8 }
137 if endswith(path,plen,".svg" as *u8,4)==1 { return "image/svg+xml" as *u8 }
138 if endswith(path,plen,".webp" as *u8,5)==1 { return "image/webp" as *u8 }
139 if endswith(path,plen,".avif" as *u8,5)==1 { return "image/avif" as *u8 }
140 // --- era images the browser will not render, but which must survive intact
141 if endswith(path,plen,".tif" as *u8,4)==1 { return "image/tiff" as *u8 }
142 if endswith(path,plen,".tiff" as *u8,5)==1 { return "image/tiff" as *u8 }
143 if endswith(path,plen,".xbm" as *u8,4)==1 { return "image/x-xbitmap" as *u8 }
144 if endswith(path,plen,".pcx" as *u8,4)==1 { return "image/x-pcx" as *u8 }
145 // --- period audio: MIDI background music was ubiquitous on the 90s web
146 if endswith(path,plen,".mid" as *u8,4)==1 { return "audio/midi" as *u8 }
147 if endswith(path,plen,".midi" as *u8,5)==1 { return "audio/midi" as *u8 }
148 if endswith(path,plen,".wav" as *u8,4)==1 { return "audio/wav" as *u8 }
149 if endswith(path,plen,".au" as *u8,3)==1 { return "audio/basic" as *u8 }
150 if endswith(path,plen,".mp3" as *u8,4)==1 { return "audio/mpeg" as *u8 }
151 // --- period video / plugin media
152 if endswith(path,plen,".swf" as *u8,4)==1 { return "application/x-shockwave-flash" as *u8 }
153 if endswith(path,plen,".avi" as *u8,4)==1 { return "video/x-msvideo" as *u8 }
154 if endswith(path,plen,".mov" as *u8,4)==1 { return "video/quicktime" as *u8 }
155 if endswith(path,plen,".mpg" as *u8,4)==1 { return "video/mpeg" as *u8 }
156 if endswith(path,plen,".mpeg" as *u8,5)==1 { return "video/mpeg" as *u8 }
157 if endswith(path,plen,".mp4" as *u8,4)==1 { return "video/mp4" as *u8 }
158 if endswith(path,plen,".rm" as *u8,3)==1 { return "application/vnd.rn-realmedia" as *u8 }
159 if endswith(path,plen,".ram" as *u8,4)==1 { return "audio/x-pn-realaudio" as *u8 }
160 if endswith(path,plen,".pdf" as *u8,4)==1 { return "application/pdf" as *u8 }
161 if endswith(path,plen,".zip" as *u8,4)==1 { return "application/zip" as *u8 }
162 return "text/plain" as *u8
163}
164func has_dotdot(buf: *u8, off: i64, len: i64) -> i64 { var i: i64=off; while i+1<off+len { if (buf[i] as i64)==0x2e { if (buf[i+1] as i64)==0x2e { return 1 } } i=i+1 } return 0 }
165// exact path match: req[pst..pst+plen) == lit (litlen bytes)
166func path_eq(req: *u8, pst: i64, plen: i64, lit: *u8, litlen: i64) -> i64 { if plen!=litlen { return 0 } var i: i64=0; while i<litlen { if (req[pst+i] as i64)!=(lit[i] as i64) { return 0 } i=i+1 } return 1 }
167func path_starts(req: *u8, pst: i64, plen: i64, lit: *u8, litlen: i64) -> i64 { if plen<litlen { return 0 } var i: i64=0; while i<litlen { if (req[pst+i] as i64)!=(lit[i] as i64) { return 0 } i=i+1 } return 1 }
168// extract the value of "id=" from query req[qst..qen) into idbuf (NUL-terminated); returns its length (0 if none)
169func qid(req: *u8, qst: i64, qen: i64, idbuf: *u8) -> i64 {
170 idbuf[0]=0 as u8
171 if qst<0 { return 0 }
172 var i: i64=qst
173 while i+2<qen { if (req[i] as i64)==0x69 { if (req[i+1] as i64)==0x64 { if (req[i+2] as i64)==0x3d {
174 var s: i64=i+3; var o: i64=0
175 while s<qen { let c: i64=req[s] as i64; if c==0x26 { s=qen } else { if o<200 { idbuf[o]=req[s]; o=o+1 } s=s+1 } }
176 idbuf[o]=0 as u8; return o
177 } } } i=i+1 }
178 return 0
179}
180
181func main() -> i64 {
182 let fd: i64=sys_socket(2, 1, 0)
183 if fd<0 { sw("socket fail\n" as *u8); return 1 }
184 let opt: *i64=sys_mmap(8) as *i64; opt[0]=1
185 __syscall(54, fd, 1, 2, opt as i64, 4, 0)
186 let addr: *u8=sys_mmap(16)
187 addr[0]=2 as u8; addr[1]=0 as u8
188 // *DERIVE the port from the const. It used to be hand-written raw bytes -- addr[2]=31, addr[3]=144,
189 // i.e. big-endian 0x1F90 = 8080 -- under a comment claiming "port K_MAGIC_8080". The comment asserted a
190 // relationship the code did not implement: the const was DEAD and the bind was independent of it.
191 // Cost: two "port changes" (8080 -> 18795 -> 18801) that edited only the dead const and moved nothing,
192 // while the daemon kept binding 8080 -- which redirect.elf holds -- and died on every spawn. The log
193 // line `bind fail (port 8080 in use?)` was accurate the whole time and was read as a stale string.
194 // *A CONSTANT THAT NOTHING READS IS NOT CONFIGURATION, IT IS A COMMENT THAT COMPILES.
195 addr[2]=((ARCSRV_PORT >> 8) & 0xff) as u8; addr[3]=(ARCSRV_PORT & 0xff) as u8
196 var z: i64=4; while z<16 { addr[z]=0 as u8; z=z+1 }
197 // *THE PORT IS PRINTED FROM THE CONST, NEVER RETYPED. Both messages below hardcoded "8080" while the
198 // bind used ARCSRV_PORT, so a CORRECT server reported a WRONG port and the failure message named a port
199 // it was not even trying to bind. Same defect class as the bind bug itself -- a literal asserting a value
200 // the code does not use -- and the reason a truthful log line got dismissed as stale for three rounds.
201 let pstr: *u8=sys_mmap(128); var pso: i64=apn(pstr,0,ARCSRV_PORT); pstr[pso]=0 as u8
202 if sys_bind(fd, addr, 16)<0 { sw("bind fail (port " as *u8); sw(pstr); sw(" in use?)\n" as *u8); return 1 }
203 if sys_listen(fd, 16)<0 { sw("listen fail\n" as *u8); return 1 }
204 sw("nx_archive_server: serving web_assets/ + /blocklist on http://localhost:" as *u8); sw(pstr); sw("/\n" as *u8)
205
206 let req: *u8=sys_mmap(K_MAGIC_8192)
207 let hdr: *u8=sys_mmap(K_MAGIC_8192)
208 let fpath: *u8=sys_mmap(K_MAGIC_2048)
209 let lb: *i64=sys_mmap(16) as *i64
210 let htmlbuf: *u8=sys_mmap(K_MAGIC_2097152)
211
212 var go: i64=1
213 while go==1 {
214 let cfd: i64=sys_accept(fd)
215 if cfd<0 { } else {
216 let rn: i64=sys_read(cfd, req, K_MAGIC_8192)
217 if rn<=0 { sys_close(cfd) } else {
218 // path = after first space, up to next space/?/end; query = after '?' up to space
219 var f1: i64=0; var i: i64=0; var g1: i64=1
220 while g1==1 { if i<rn { if (req[i] as i64)==0x20 { f1=i; g1=0 } else { i=i+1 } } else { f1=rn; g1=0 } }
221 var pst: i64=f1+1
222 var pen: i64=pst; var j: i64=pst; var g2: i64=1
223 while g2==1 { if j<rn { let c: i64=req[j] as i64; if c==0x20 { pen=j; g2=0 } else { if c==0x3f { pen=j; g2=0 } else { j=j+1 } } } else { pen=j; g2=0 } }
224 let plen: i64=pen-pst
225 var qst: i64=0-1; var qen: i64=0-1
226 if pen<rn { if (req[pen] as i64)==0x3f { qst=pen+1; var jj: i64=qst; var g3: i64=1; while g3==1 { if jj<rn { if (req[jj] as i64)==0x20 { qen=jj; g3=0 } else { jj=jj+1 } } else { qen=jj; g3=0 } } } }
227
228 var routed: i64=0
229 if has_dotdot(req, pst, plen)==1 {
230 var ho: i64=ap(hdr,0,"HTTP/1.1 403 Forbidden\r\nContent-Length: 9\r\nConnection: close\r\n\r\nForbidden" as *u8)
231 sys_write(cfd, hdr, ho); sys_close(cfd); routed=1
232 }
233 // /blocklist -- live supervised view rendered from seg_store
234 if routed==0 { if path_eq(req,pst,plen,"/blocklist" as *u8,10)==1 {
235 let bo: i64=bl_render_html(BL_PREFIX, htmlbuf)
236 var ho: i64=ap(hdr,0,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: " as *u8)
237 ho=apn(hdr,ho,bo); ho=ap(hdr,ho,"\r\nConnection: close\r\n\r\n" as *u8)
238 sys_write(cfd, hdr, ho); sys_write(cfd, htmlbuf, bo); sys_close(cfd); routed=1
239 } }
240 // /blocklist/approve?id=N -- operator ACTION: flip to approved, then back to the list
241 if routed==0 { if path_starts(req,pst,plen,"/blocklist/approve" as *u8,18)==1 {
242 let idb: *u8=sys_mmap(256); qid(req, qst, qen, idb)
243 if (idb[0] as i64)!=0 { bl_approve(idb) }
244 var ho: i64=ap(hdr,0,"HTTP/1.1 303 See Other\r\nLocation: /blocklist\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" as *u8)
245 sys_write(cfd, hdr, ho); sys_close(cfd); routed=1
246 } }
247 // /blocklist/retract?id=N -- operator ACTION: retract (stops blocking; history kept)
248 if routed==0 { if path_starts(req,pst,plen,"/blocklist/retract" as *u8,18)==1 {
249 let idb: *u8=sys_mmap(256); qid(req, qst, qen, idb)
250 if (idb[0] as i64)!=0 { bl_retract(idb) }
251 var ho: i64=ap(hdr,0,"HTTP/1.1 303 See Other\r\nLocation: /blocklist\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" as *u8)
252 sys_write(cfd, hdr, ho); sys_close(cfd); routed=1
253 } }
254 // "/" -> landing page
255 if routed==0 { if plen<=1 {
256 var bo: i64=0
257 bo=ap(req,bo,"<!DOCTYPE html><meta charset=utf-8><title>Nishi Time Machine</title><body style='font-family:system-ui;background:#0e0f13;color:#e7e7ea;max-width:760px;margin:40px auto;padding:0 20px'><h1>🕐 Nishi Time Machine</h1><ul style='line-height:2'>" as *u8)
258 bo=ap(req,bo,"<li><a href=/archive/page3.com/index.html><b>page3.com</b> — scroll the history (134 captures 1996-2003), local media</a>" as *u8)
259 bo=ap(req,bo,"<li><a href=/archive/megastar.co.uk/index.html><b>megastar.co.uk</b> — scroll the history (61 captures, 1998+)</a>" as *u8)
260 bo=ap(req,bo,"<li><a href=/archive/recovery.html>media recovery — perceptual fingerprint + CID dedup</a>" as *u8)
261 bo=ap(req,bo,"<li><a href=/blocklist><b>do-not-recover blocklist</b> — supervised, justified, approve on the page</a>" as *u8)
262 bo=ap(req,bo,"</ul><p style=color:#8a8f9a>Served sovereignly — our own TLS-1.3 fetch, parsers, image decode, seg_store, and this HTTP server.</p></body>" as *u8)
263 var ho: i64=ap(hdr,0,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: " as *u8)
264 ho=apn(hdr,ho,bo); ho=ap(hdr,ho,"\r\nConnection: close\r\n\r\n" as *u8)
265 sys_write(cfd, hdr, ho); sys_write(cfd, req, bo); sys_close(cfd); routed=1
266 } }
267 // static file under web_assets/
268 if routed==0 {
269 var fp: i64=ap(fpath,0,"web_assets" as *u8)
270 var k: i64=pst
271 while k<pen { fpath[fp]=req[k]; fp=fp+1; k=k+1 }
272 fpath[fp]=0 as u8
273 // DIRECTORY FIRST. getdents64 refuses a regular file (-ENOTDIR), so this is a single
274 // call that both classifies and reads -- and it runs BEFORE sys_read_file, because
275 // sys_read_file SUCCEEDS on a directory with 0 bytes and that is exactly the empty-200.
276 let dl: i64=as_dirlist(fpath, req, pst, pen, htmlbuf)
277 if dl>0 {
278 var dho: i64=ap(hdr,0,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: " as *u8)
279 dho=apn(hdr,dho,dl); dho=ap(hdr,dho,"\r\nConnection: close\r\n\r\n" as *u8)
280 sys_write(cfd, hdr, dho); sys_write(cfd, htmlbuf, dl); sys_close(cfd)
281 } else {
282 let body: *u8=sys_read_file(fpath, lb)
283 if body==(0 as *u8) {
284 var ho: i64=ap(hdr,0,"HTTP/1.1 404 Not Found\r\nContent-Length: 9\r\nConnection: close\r\n\r\nNot found" as *u8)
285 sys_write(cfd, hdr, ho); sys_close(cfd)
286 } else {
287 let fn: i64=lb[0]
288 // bind the const to a LOCAL pointer before indexing it -- a direct CONST[i] read can
289 // compile clean and then fetch garbage (banked nx_cc gotcha), and this one sizes a
290 // Content-Length, so getting it wrong truncates the page rather than failing loudly.
291 let tg: *u8 = ARC_TOGGLE
292 var inj: i64=0
293 if endswith(fpath, fp, ".html" as *u8, 5)==1 { inj=1 }
294 if endswith(fpath, fp, ".htm" as *u8, 4)==1 { inj=1 }
295 var extra: i64=0
296 if inj==1 { var t: i64=0; while tg[t]!=(0 as u8) { t=t+1 } extra=t }
297 var ho: i64=ap(hdr,0,"HTTP/1.1 200 OK\r\nContent-Type: " as *u8)
298 ho=ap(hdr,ho,ctype(fpath,fp)); ho=ap(hdr,ho,"\r\nContent-Length: " as *u8); ho=apn(hdr,ho,fn+extra); ho=ap(hdr,ho,"\r\nConnection: close\r\n\r\n" as *u8)
299 sys_write(cfd, hdr, ho)
300 var sent: i64=0
301 while sent<fn { let wn: i64=sys_write(cfd, ((body as i64)+sent) as *u8, fn-sent); if wn<=0 { sent=fn } else { sent=sent+wn } }
302 if inj==1 { var es: i64=0; while es<extra { let ew: i64=sys_write(cfd, ((tg as i64)+es) as *u8, extra-es); if ew<=0 { es=extra } else { es=es+ew } } }
303 sys_close(cfd)
304 }
305 }
306 }
307 }
308 }
309 }
310 return 0
311}