code wiki / _hdl_build / nx_email_webmail_gate.nx
nx_email_webmail_gate.nx source
↩ module page · 157 lines · 6309 B
1// nx_email_webmail_gate.nx -- LIVE: the webmail UI over a real HTTP socket.
2//
3// Seeds the R3 store with 2 messages for self@jasonewest.com, then fork:
4// parent = the webmail server (nx_webmail_serve_conn over accept), child =
5// a real HTTP/1.1 client making 3 GETs:
6// 1. GET / -> HTML lists BOTH subjects ("Welcome" + "Second Note")
7// 2. GET /del?n=0 -> redraw NO LONGER shows "Welcome" (soft-deleted)
8// 3. GET / -> still no "Welcome", still "Second Note"
9// Then the parent verifies the store: message #0 tombstoned, #1 live.
10//
11// Evidence -> knowledge/status/webmail.log (WEBMAILGATE ... verdict=GREEN)
12// license_tier: ORIGINAL
13import "nx_email_webmail.nx"
14import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
15import "nx_syscalls.nx"
16
17const WM_PORT: i64 = 0x2555
18const WM_LOG: *u8 = "knowledge/status/webmail.log"
19const WM_PREFIX: *u8 = "knowledge/status/wmail/"
20const WM_BOX: *u8 = "self@jasonewest.com"
21
22func g_addr(out: *u8, port: i64) -> i64 {
23 out[0] = 2 as u8; out[1] = 0 as u8
24 out[2] = ((port >> 8) & 0xff) as u8; out[3] = (port & 0xff) as u8
25 out[4] = 127 as u8; out[5] = 0 as u8; out[6] = 0 as u8; out[7] = 1 as u8
26 var i: i64 = 8
27 while i < 16 { out[i] = 0 as u8; i = i + 1 }
28 return 0
29}
30func g_writes(fd: i64, s: *u8) -> i64 {
31 var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 }
32 var w: i64 = 0
33 while w < n { let k: i64 = sys_write(fd, (s as i64 + w) as *u8, n - w); if k <= 0 { return 0 - 1 } w = w + k }
34 return 0
35}
36func slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
37func contains(hay: *u8, hlen: i64, needle: *u8) -> i64 {
38 let nl: i64 = slen(needle)
39 var i: i64 = 0
40 while i + nl <= hlen {
41 var k: i64 = 0; var hit: i64 = 1
42 while k < nl { if (hay[i + k] & 0xff) != (needle[k] & 0xff) { hit = 0; k = nl } else { k = k + 1 } }
43 if hit == 1 { return 1 }
44 i = i + 1
45 }
46 return 0
47}
48// one HTTP GET; reads the whole response (Connection: close) into out. len.
49func http_get(addr: *u8, path: *u8, out: *u8, cap: i64) -> i64 {
50 let cfd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0)
51 if cfd < 0 { return 0 - 1 }
52 if nx_connect_bounded(cfd, addr, 16, NX_CONN_DEFAULT_MS) < 0 { return 0 - 2 }
53 g_writes(cfd, "GET " as *u8)
54 g_writes(cfd, path)
55 g_writes(cfd, " HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n" as *u8)
56 var n: i64 = 0
57 while n < cap - 1 {
58 let r: i64 = sys_read(cfd, (out as i64 + n) as *u8, cap - 1 - n)
59 if r <= 0 { n = n } else { n = n + r }
60 if r <= 0 { sys_close(cfd); return n }
61 }
62 sys_close(cfd)
63 return n
64}
65
66func g_run_client(addr: *u8) -> i64 {
67 var spin: i64 = 0
68 while spin < 400000 { spin = spin + 1 }
69 let buf: *u8 = sys_mmap(262144)
70 // 1. inbox shows both
71 let n1: i64 = http_get(addr, "/" as *u8, buf, 262144)
72 if n1 <= 0 { return 11 }
73 if contains(buf, n1, "Welcome" as *u8) != 1 { return 12 }
74 if contains(buf, n1, "Second Note" as *u8) != 1 { return 13 }
75 // 2. delete #0
76 let n2: i64 = http_get(addr, "/del?n=0" as *u8, buf, 262144)
77 if n2 <= 0 { return 14 }
78 if contains(buf, n2, "Welcome" as *u8) != 0 { return 15 } // gone
79 if contains(buf, n2, "Second Note" as *u8) != 1 { return 16 } // remains
80 // 3. inbox redraw confirms
81 let n3: i64 = http_get(addr, "/" as *u8, buf, 262144)
82 if n3 <= 0 { return 17 }
83 if contains(buf, n3, "Welcome" as *u8) != 0 { return 18 }
84 if contains(buf, n3, "Second Note" as *u8) != 1 { return 19 }
85 return 0
86}
87
88func main() -> i64 {
89 sys_mkdir("knowledge/status/wmail" as *u8, 511)
90 let rfd: i64 = sys_openat_wr("knowledge/status/wmail/manifest.txt" as *u8, 420)
91 if rfd >= 0 { sys_close(rfd) }
92
93 // seed two messages
94 let m0: *u8 = "Subject: Welcome\r\nFrom: ops@jasonewest.com\r\n\r\nWelcome to Nishi Mail.\r\n" as *u8
95 let m1: *u8 = "Subject: Second Note\r\nFrom: alice@remote.test\r\n\r\nSecond message body here.\r\n" as *u8
96 nx_mbox_append(WM_PREFIX, WM_BOX, m0, slen(m0), 1)
97 nx_mbox_append(WM_PREFIX, WM_BOX, m1, slen(m1), 2)
98
99 let segctr: *i64 = sys_mmap(8) as *i64
100 segctr[0] = 100
101
102 let addr: *u8 = sys_mmap(16)
103 g_addr(addr, WM_PORT)
104 let lfd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0)
105 if lfd < 0 { sys_exit(93); return 93 }
106 if sys_bind(lfd, addr, 16) < 0 { sys_exit(94); return 94 }
107 if sys_listen(lfd, 8) < 0 { sys_exit(95); return 95 }
108
109 let pid: i64 = sys_fork()
110 if pid < 0 { sys_exit(96); return 96 }
111 if pid == 0 {
112 let crc: i64 = g_run_client(addr)
113 sys_exit(crc); return crc
114 }
115
116 // parent serves 3 connections
117 var served: i64 = 0
118 while served < 3 {
119 let scfd: i64 = sys_accept(lfd)
120 if scfd < 0 { served = 3 } else {
121 nx_webmail_serve_conn(scfd, WM_PREFIX, WM_BOX, segctr)
122 sys_close(scfd)
123 served = served + 1
124 }
125 }
126
127 let status: *i64 = sys_mmap(8) as *i64
128 sys_wait4(pid, status, 0)
129 let child_code: i64 = (status[0] >> 8) & 0xff
130
131 // verify store: #0 tombstoned, #1 live
132 let pp: *i64 = sys_mmap(8) as *i64
133 let ll: *i64 = sys_mmap(8) as *i64
134 let g0: i64 = nx_mbox_get_n(WM_PREFIX, WM_BOX, 0, pp, ll)
135 let g1: i64 = nx_mbox_get_n(WM_PREFIX, WM_BOX, 1, pp, ll)
136 var store_ok: i64 = 0
137 if g0 == 0 && g1 == 1 { store_ok = 1 }
138
139 var green: i64 = 0
140 if child_code == 0 && store_ok == 1 { green = 1 }
141
142 var fd: i64 = 1
143 while fd >= 1 {
144 g_writes(fd, "WEBMAILGATE authored=organ http=loopback mailbox=self@jasonewest.com " as *u8)
145 if child_code == 0 { g_writes(fd, "list_shows_both=PASS delete_works=PASS redraw_reflects_delete=PASS " as *u8) }
146 else { g_writes(fd, "client_checks=FAIL " as *u8) }
147 if store_ok == 1 { g_writes(fd, "store_tombstoned=PASS " as *u8) } else { g_writes(fd, "store_tombstoned=FAIL " as *u8) }
148 if green == 1 { g_writes(fd, "verdict=GREEN\n" as *u8) } else { g_writes(fd, "verdict=RED\n" as *u8) }
149 if fd == 1 {
150 let lf: i64 = sys_openat_append(WM_LOG, 420)
151 if lf >= 1 { fd = lf } else { fd = 0 }
152 } else { sys_close(fd); fd = 0 }
153 }
154
155 if green == 1 { sys_exit(0); return 0 }
156 sys_exit(1); return 1
157}