Blame


1 04e4e993 2023-08-14 op /*
2 04e4e993 2023-08-14 op * Copyright (c) 2023 Omar Polo <op@omarpolo.com>
3 04e4e993 2023-08-14 op * Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
4 04e4e993 2023-08-14 op *
5 04e4e993 2023-08-14 op * Permission to use, copy, modify, and distribute this software for any
6 04e4e993 2023-08-14 op * purpose with or without fee is hereby granted, provided that the above
7 04e4e993 2023-08-14 op * copyright notice and this permission notice appear in all copies.
8 04e4e993 2023-08-14 op *
9 04e4e993 2023-08-14 op * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 04e4e993 2023-08-14 op * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 04e4e993 2023-08-14 op * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 04e4e993 2023-08-14 op * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 04e4e993 2023-08-14 op * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 04e4e993 2023-08-14 op * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 04e4e993 2023-08-14 op * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 04e4e993 2023-08-14 op */
17 04e4e993 2023-08-14 op
18 04e4e993 2023-08-14 op #include "config.h"
19 04e4e993 2023-08-14 op
20 04e4e993 2023-08-14 op #include <sys/socket.h>
21 04e4e993 2023-08-14 op #include <sys/types.h>
22 04e4e993 2023-08-14 op #include <sys/un.h>
23 04e4e993 2023-08-14 op
24 04e4e993 2023-08-14 op #include <ctype.h>
25 04e4e993 2023-08-14 op #include <errno.h>
26 04e4e993 2023-08-14 op #include <fnmatch.h>
27 04e4e993 2023-08-14 op #include <limits.h>
28 04e4e993 2023-08-14 op #include <locale.h>
29 04e4e993 2023-08-14 op #include <netdb.h>
30 04e4e993 2023-08-14 op #include <signal.h>
31 04e4e993 2023-08-14 op #include <stdio.h>
32 04e4e993 2023-08-14 op #include <stdlib.h>
33 04e4e993 2023-08-14 op #include <string.h>
34 04e4e993 2023-08-14 op #include <syslog.h>
35 04e4e993 2023-08-14 op #include <unistd.h>
36 04e4e993 2023-08-14 op
37 04e4e993 2023-08-14 op #include "amused.h"
38 3634fa70 2023-08-31 op #include "bufio.h"
39 80d5f5ad 2023-08-25 op #include "ev.h"
40 04e4e993 2023-08-14 op #include "http.h"
41 04e4e993 2023-08-14 op #include "log.h"
42 04e4e993 2023-08-14 op #include "playlist.h"
43 b42d807f 2023-09-02 op #include "ws.h"
44 04e4e993 2023-08-14 op #include "xmalloc.h"
45 04e4e993 2023-08-14 op
46 04e4e993 2023-08-14 op #ifndef nitems
47 04e4e993 2023-08-14 op #define nitems(x) (sizeof(x)/sizeof(x[0]))
48 04e4e993 2023-08-14 op #endif
49 04e4e993 2023-08-14 op
50 04e4e993 2023-08-14 op #define FORM_URLENCODED "application/x-www-form-urlencoded"
51 04e4e993 2023-08-14 op
52 04e4e993 2023-08-14 op #define ICON_REPEAT_ALL "🔁"
53 04e4e993 2023-08-14 op #define ICON_REPEAT_ONE "🔂"
54 04e4e993 2023-08-14 op #define ICON_PREV "⏮"
55 04e4e993 2023-08-14 op #define ICON_NEXT "⏭"
56 04e4e993 2023-08-14 op #define ICON_STOP "⏹"
57 04e4e993 2023-08-14 op #define ICON_PAUSE "⏸"
58 04e4e993 2023-08-14 op #define ICON_TOGGLE "⏯"
59 04e4e993 2023-08-14 op #define ICON_PLAY "⏵"
60 04e4e993 2023-08-14 op
61 b42d807f 2023-09-02 op static struct clthead clients;
62 50e0da0e 2024-01-21 op static struct imsgbuf imsgbuf;
63 20dff2d2 2023-08-31 op static struct playlist playlist_tmp;
64 20dff2d2 2023-08-31 op static struct player_status player_status;
65 20dff2d2 2023-08-31 op static uint64_t position, duration;
66 04e4e993 2023-08-14 op
67 b42d807f 2023-09-02 op static void client_ev(int, int, void *);
68 b42d807f 2023-09-02 op
69 04e4e993 2023-08-14 op const char *head = "<!doctype html>"
70 04e4e993 2023-08-14 op "<html>"
71 04e4e993 2023-08-14 op "<head>"
72 04e4e993 2023-08-14 op "<meta name='viewport' content='width=device-width, initial-scale=1'/>"
73 04e4e993 2023-08-14 op "<title>Amused Web</title>"
74 cc932200 2023-08-31 op "<link rel='stylesheet' href='/style.css?v=0'>"
75 cc932200 2023-08-31 op "</style>"
76 cc932200 2023-08-31 op "</head>"
77 cc932200 2023-08-31 op "<body>";
78 cc932200 2023-08-31 op
79 cc932200 2023-08-31 op const char *css = "*{box-sizing:border-box}"
80 04e4e993 2023-08-14 op "html,body{"
81 04e4e993 2023-08-14 op " padding: 0;"
82 04e4e993 2023-08-14 op " border: 0;"
83 04e4e993 2023-08-14 op " margin: 0;"
84 04e4e993 2023-08-14 op "}"
85 04e4e993 2023-08-14 op "main{"
86 04e4e993 2023-08-14 op " display: flex;"
87 04e4e993 2023-08-14 op " flex-direction: column;"
88 04e4e993 2023-08-14 op "}"
89 04e4e993 2023-08-14 op "button{cursor:pointer}"
90 04e4e993 2023-08-14 op ".searchbox{"
91 04e4e993 2023-08-14 op " position: sticky;"
92 04e4e993 2023-08-14 op " top: 0;"
93 04e4e993 2023-08-14 op "}"
94 04e4e993 2023-08-14 op ".searchbox input{"
95 04e4e993 2023-08-14 op " width: 100%;"
96 04e4e993 2023-08-14 op " padding: 9px;"
97 04e4e993 2023-08-14 op "}"
98 04e4e993 2023-08-14 op ".playlist-wrapper{min-height:80vh}"
99 04e4e993 2023-08-14 op ".playlist{"
100 04e4e993 2023-08-14 op " list-style: none;"
101 04e4e993 2023-08-14 op " padding: 0;"
102 04e4e993 2023-08-14 op " margin: 0;"
103 04e4e993 2023-08-14 op "}"
104 04e4e993 2023-08-14 op ".playlist button{"
105 04e4e993 2023-08-14 op " font-family: monospace;"
106 04e4e993 2023-08-14 op " text-align: left;"
107 04e4e993 2023-08-14 op " width: 100%;"
108 04e4e993 2023-08-14 op " padding: 5px;"
109 04e4e993 2023-08-14 op " border: 0;"
110 04e4e993 2023-08-14 op " background: transparent;"
111 04e4e993 2023-08-14 op " transition: background-color .25s ease-in-out;"
112 04e4e993 2023-08-14 op "}"
113 04e4e993 2023-08-14 op ".playlist button::before{"
114 04e4e993 2023-08-14 op " content: \"\";"
115 04e4e993 2023-08-14 op " width: 2ch;"
116 04e4e993 2023-08-14 op " display: inline-block;"
117 04e4e993 2023-08-14 op "}"
118 04e4e993 2023-08-14 op ".playlist button:hover{"
119 04e4e993 2023-08-14 op " background-color: #dfdddd;"
120 04e4e993 2023-08-14 op "}"
121 04e4e993 2023-08-14 op ".playlist #current button{"
122 04e4e993 2023-08-14 op " font-weight: bold;"
123 04e4e993 2023-08-14 op "}"
124 04e4e993 2023-08-14 op ".playlist #current button::before{"
125 04e4e993 2023-08-14 op " content: \"→ \";"
126 04e4e993 2023-08-14 op " font-weight: bold;"
127 04e4e993 2023-08-14 op "}"
128 04e4e993 2023-08-14 op ".controls{"
129 04e4e993 2023-08-14 op " position: sticky;"
130 04e4e993 2023-08-14 op " width: 100%;"
131 04e4e993 2023-08-14 op " max-width: 800px;"
132 04e4e993 2023-08-14 op " margin: 0 auto;"
133 04e4e993 2023-08-14 op " bottom: 0;"
134 04e4e993 2023-08-14 op " background-color: white;"
135 04e4e993 2023-08-14 op " background: #3d3d3d;"
136 04e4e993 2023-08-14 op " color: white;"
137 04e4e993 2023-08-14 op " border-radius: 10px 10px 0 0;"
138 04e4e993 2023-08-14 op " padding: 10px;"
139 04e4e993 2023-08-14 op " text-align: center;"
140 04e4e993 2023-08-14 op " order: 2;"
141 04e4e993 2023-08-14 op "}"
142 04e4e993 2023-08-14 op ".controls p{"
143 04e4e993 2023-08-14 op " margin: .4rem;"
144 04e4e993 2023-08-14 op "}"
145 04e4e993 2023-08-14 op ".controls a{"
146 04e4e993 2023-08-14 op " color: white;"
147 04e4e993 2023-08-14 op "}"
148 04e4e993 2023-08-14 op ".controls .status{"
149 04e4e993 2023-08-14 op " font-size: 0.9rem;"
150 04e4e993 2023-08-14 op "}"
151 04e4e993 2023-08-14 op ".controls button{"
152 04e4e993 2023-08-14 op " margin: 5px;"
153 04e4e993 2023-08-14 op " padding: 5px 20px;"
154 04e4e993 2023-08-14 op "}"
155 04e4e993 2023-08-14 op ".mode-active{"
156 04e4e993 2023-08-14 op " color: #0064ff;"
157 cc932200 2023-08-31 op "}";
158 04e4e993 2023-08-14 op
159 cc932200 2023-08-31 op const char *js =
160 b42d807f 2023-09-02 op "var ws;"
161 b42d807f 2023-09-02 op "let pos=0, dur=0;"
162 b42d807f 2023-09-02 op "const playlist=document.querySelector('.playlist');"
163 04e4e993 2023-08-14 op "function cur(e) {"
164 04e4e993 2023-08-14 op " if (e) {e.preventDefault()}"
165 04e4e993 2023-08-14 op " let cur = document.querySelector('#current');"
166 04e4e993 2023-08-14 op " if (cur) {cur.scrollIntoView(); window.scrollBy(0, -100);}"
167 b42d807f 2023-09-02 op "};"
168 b42d807f 2023-09-02 op "function b(x){return x=='on'};"
169 b42d807f 2023-09-02 op "function c(p, c){"
170 b42d807f 2023-09-02 op " const l=document.createElement('li');"
171 b42d807f 2023-09-02 op " if(c){l.id='current'};"
172 b42d807f 2023-09-02 op " const b=document.createElement('button');"
173 b42d807f 2023-09-02 op " b.type='submit'; b.name='jump'; b.value=p;"
174 b42d807f 2023-09-02 op " b.innerText=p;"
175 b42d807f 2023-09-02 op " l.appendChild(b);"
176 b42d807f 2023-09-02 op " playlist.appendChild(l);"
177 04e4e993 2023-08-14 op "}"
178 b42d807f 2023-09-02 op "function d(t){"
179 b42d807f 2023-09-02 op " const [, type, payload] = t.split(/^(.):(.*)$/);"
180 b42d807f 2023-09-02 op " if (type=='s'){"
181 b42d807f 2023-09-02 op " let s=payload.split(' ');"
182 b42d807f 2023-09-02 op " pos=s[0], dur=s[1];"
183 b42d807f 2023-09-02 op " } else if (type=='S') {"
184 b42d807f 2023-09-02 op " const btn=document.querySelector('#toggle');"
185 b42d807f 2023-09-02 op " if (payload=='playing') {"
186 b42d807f 2023-09-02 op " btn.innerHTML='"ICON_PAUSE"';"
187 b42d807f 2023-09-02 op " btn.value='pause';"
188 b42d807f 2023-09-02 op " } else {"
189 b42d807f 2023-09-02 op " btn.innerHTML='"ICON_PLAY"';"
190 b42d807f 2023-09-02 op " btn.value='play';"
191 b42d807f 2023-09-02 op " }"
192 b42d807f 2023-09-02 op " } else if (type=='r') {"
193 b42d807f 2023-09-02 op " const btn=document.querySelector('#rone');"
194 b42d807f 2023-09-02 op " btn.className=b(payload)?'mode-active':'';"
195 b42d807f 2023-09-02 op " } else if (type=='R') {"
196 b42d807f 2023-09-02 op " const btn=document.querySelector('#rall');"
197 b42d807f 2023-09-02 op " btn.className=b(payload)?'mode-active':'';"
198 b42d807f 2023-09-02 op " } else if (type=='c') {"
199 b42d807f 2023-09-02 op /* consume */
200 b42d807f 2023-09-02 op " } else if (type=='x') {"
201 b42d807f 2023-09-02 op " playlist.innerHTML='';"
202 0e016c89 2023-09-07 op " } else if (type=='X') {"
203 0e016c89 2023-09-07 op " dofilt();" /* done with the list */
204 b42d807f 2023-09-02 op " } else if (type=='A') {"
205 b42d807f 2023-09-02 op " c(payload, true);"
206 b42d807f 2023-09-02 op " } else if (type=='a') {"
207 b42d807f 2023-09-02 op " c(payload, false);"
208 b42d807f 2023-09-02 op " } else if (type=='C') {"
209 b42d807f 2023-09-02 op " const t=document.querySelector('.controls>p>a');"
210 b42d807f 2023-09-02 op " t.innerText = payload.replace(/.*\\//, '');"
211 b42d807f 2023-09-02 op " cur();"
212 b42d807f 2023-09-02 op " } else {"
213 b42d807f 2023-09-02 op " console.log('unknown:',t);"
214 b42d807f 2023-09-02 op " }"
215 b42d807f 2023-09-02 op "};"
216 b42d807f 2023-09-02 op "function w(){"
217 b42d807f 2023-09-02 op " ws = new WebSocket((location.protocol=='http:'?'ws://':'wss://')"
218 b42d807f 2023-09-02 op " + location.host + '/ws');"
219 b42d807f 2023-09-02 op " ws.addEventListener('open', () => console.log('ws: connected'));"
220 ed3946c3 2023-09-07 op " ws.addEventListener('close', () => {"
221 ed3946c3 2023-09-07 op " alert('Websocket closed. The interface won\\'t update itself.'"
222 ed3946c3 2023-09-07 op " + ' Please refresh the page');"
223 ed3946c3 2023-09-07 op " });"
224 b42d807f 2023-09-02 op " ws.addEventListener('message', e => d(e.data))"
225 b42d807f 2023-09-02 op "};"
226 b42d807f 2023-09-02 op "w();"
227 04e4e993 2023-08-14 op "cur();"
228 b42d807f 2023-09-02 op "document.querySelector('.controls a').addEventListener('click',cur);"
229 b42d807f 2023-09-02 op "document.querySelectorAll('form').forEach(f => {"
230 b42d807f 2023-09-02 op " f.action='/a/'+f.getAttribute('action');"
231 b42d807f 2023-09-02 op " f.addEventListener('submit', e => {"
232 b42d807f 2023-09-02 op " e.preventDefault();"
233 b42d807f 2023-09-02 op " const fd = new FormData(f);"
234 b42d807f 2023-09-02 op " if (e.submitter && e.submitter.value && e.submitter.value != '')"
235 b42d807f 2023-09-02 op " fd.append(e.submitter.name, e.submitter.value);"
236 b42d807f 2023-09-02 op " fetch(f.action, {"
237 b42d807f 2023-09-02 op " method:'POST',"
238 b42d807f 2023-09-02 op " body: new URLSearchParams(fd)"
239 b42d807f 2023-09-02 op " })"
240 b42d807f 2023-09-02 op " .catch(x => console.log('failed to submit form:', x));"
241 b42d807f 2023-09-02 op " });"
242 0e016c89 2023-09-07 op "});"
243 0e016c89 2023-09-07 op "const sb = document.createElement('section');"
244 0e016c89 2023-09-07 op "sb.className = 'searchbox';"
245 0e016c89 2023-09-07 op "const filter = document.createElement('input');"
246 0e016c89 2023-09-07 op "filter.type = 'search';"
247 0e016c89 2023-09-07 op "filter.setAttribute('aria-label', 'Filter Playlist');"
248 0e016c89 2023-09-07 op "filter.placeholder = 'Filter Playlist';"
249 0e016c89 2023-09-07 op "sb.append(filter);"
250 0e016c89 2023-09-07 op "document.querySelector('main').prepend(sb);"
251 0e016c89 2023-09-07 op "function dofilt() {"
252 0e016c89 2023-09-07 op " let t = filter.value.toLowerCase();"
253 0e016c89 2023-09-07 op " document.querySelectorAll('.playlist li').forEach(e => {"
254 0e016c89 2023-09-07 op " if (e.querySelector('button').value.toLowerCase().indexOf(t) == -1)"
255 0e016c89 2023-09-07 op " e.setAttribute('hidden', 'true');"
256 0e016c89 2023-09-07 op " else"
257 0e016c89 2023-09-07 op " e.removeAttribute('hidden');"
258 0e016c89 2023-09-07 op " });"
259 0e016c89 2023-09-07 op "};"
260 0e016c89 2023-09-07 op "function dbc(fn, wait) {"
261 0e016c89 2023-09-07 op " let tout;"
262 0e016c89 2023-09-07 op " return function() {"
263 0e016c89 2023-09-07 op " let later = () => {tout = null; fn()};"
264 0e016c89 2023-09-07 op " clearTimeout(tout);"
265 0e016c89 2023-09-07 op " if (!tout) fn();"
266 0e016c89 2023-09-07 op " tout = setTimeout(later, wait);"
267 0e016c89 2023-09-07 op " };"
268 0e016c89 2023-09-07 op "};"
269 0e016c89 2023-09-07 op "filter.addEventListener('input', dbc(dofilt, 400));"
270 0e016c89 2023-09-07 op ;
271 04e4e993 2023-08-14 op
272 cc932200 2023-08-31 op const char *foot = "<script src='/app.js?v=0'></script></body></html>";
273 cc932200 2023-08-31 op
274 6be81433 2024-04-14 op static inline int
275 6be81433 2024-04-14 op bio_ev(struct bufio *bio)
276 6be81433 2024-04-14 op {
277 6be81433 2024-04-14 op int ret, ev;
278 6be81433 2024-04-14 op
279 6be81433 2024-04-14 op ret = 0;
280 6be81433 2024-04-14 op ev = bufio_ev(bio);
281 6be81433 2024-04-14 op if (ev & BUFIO_WANT_READ)
282 6be81433 2024-04-14 op ret |= EV_READ;
283 6be81433 2024-04-14 op if (ev & BUFIO_WANT_WRITE)
284 6be81433 2024-04-14 op ret |= EV_WRITE;
285 6be81433 2024-04-14 op return ret;
286 6be81433 2024-04-14 op }
287 6be81433 2024-04-14 op
288 04e4e993 2023-08-14 op static int
289 04e4e993 2023-08-14 op dial(const char *sock)
290 04e4e993 2023-08-14 op {
291 04e4e993 2023-08-14 op struct sockaddr_un sa;
292 04e4e993 2023-08-14 op size_t len;
293 04e4e993 2023-08-14 op int s;
294 04e4e993 2023-08-14 op
295 04e4e993 2023-08-14 op memset(&sa, 0, sizeof(sa));
296 04e4e993 2023-08-14 op sa.sun_family = AF_UNIX;
297 04e4e993 2023-08-14 op len = strlcpy(sa.sun_path, sock, sizeof(sa.sun_path));
298 04e4e993 2023-08-14 op if (len >= sizeof(sa.sun_path))
299 04e4e993 2023-08-14 op err(1, "path too long: %s", sock);
300 04e4e993 2023-08-14 op
301 04e4e993 2023-08-14 op if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
302 04e4e993 2023-08-14 op err(1, "socket");
303 04e4e993 2023-08-14 op if (connect(s, (struct sockaddr *)&sa, sizeof(sa)) == -1)
304 04e4e993 2023-08-14 op err(1, "failed to connect to %s", sock);
305 04e4e993 2023-08-14 op
306 04e4e993 2023-08-14 op return s;
307 04e4e993 2023-08-14 op }
308 04e4e993 2023-08-14 op
309 04e4e993 2023-08-14 op /*
310 04e4e993 2023-08-14 op * Adapted from usr.sbin/httpd/httpd.c' url_decode.
311 04e4e993 2023-08-14 op */
312 04e4e993 2023-08-14 op static int
313 04e4e993 2023-08-14 op url_decode(char *url)
314 04e4e993 2023-08-14 op {
315 04e4e993 2023-08-14 op char*p, *q;
316 04e4e993 2023-08-14 op char hex[3] = {0};
317 04e4e993 2023-08-14 op unsigned long x;
318 04e4e993 2023-08-14 op
319 04e4e993 2023-08-14 op p = q = url;
320 04e4e993 2023-08-14 op while (*p != '\0') {
321 04e4e993 2023-08-14 op switch (*p) {
322 04e4e993 2023-08-14 op case '%':
323 04e4e993 2023-08-14 op /* Encoding character is followed by two hex chars */
324 04e4e993 2023-08-14 op if (!isxdigit((unsigned char)p[1]) ||
325 04e4e993 2023-08-14 op !isxdigit((unsigned char)p[2]) ||
326 04e4e993 2023-08-14 op (p[1] == '0' && p[2] == '0'))
327 04e4e993 2023-08-14 op return (-1);
328 04e4e993 2023-08-14 op
329 04e4e993 2023-08-14 op hex[0] = p[1];
330 04e4e993 2023-08-14 op hex[1] = p[2];
331 04e4e993 2023-08-14 op
332 04e4e993 2023-08-14 op /*
333 04e4e993 2023-08-14 op * We don't have to validate "hex" because it is
334 04e4e993 2023-08-14 op * guaranteed to include two hex chars followed
335 04e4e993 2023-08-14 op * by NUL.
336 04e4e993 2023-08-14 op */
337 04e4e993 2023-08-14 op x = strtoul(hex, NULL, 16);
338 04e4e993 2023-08-14 op *q = (char)x;
339 04e4e993 2023-08-14 op p += 2;
340 04e4e993 2023-08-14 op break;
341 04e4e993 2023-08-14 op case '+':
342 04e4e993 2023-08-14 op *q = ' ';
343 04e4e993 2023-08-14 op break;
344 04e4e993 2023-08-14 op default:
345 04e4e993 2023-08-14 op *q = *p;
346 04e4e993 2023-08-14 op break;
347 04e4e993 2023-08-14 op }
348 04e4e993 2023-08-14 op p++;
349 04e4e993 2023-08-14 op q++;
350 04e4e993 2023-08-14 op }
351 04e4e993 2023-08-14 op *q = '\0';
352 04e4e993 2023-08-14 op
353 04e4e993 2023-08-14 op return (0);
354 04e4e993 2023-08-14 op }
355 04e4e993 2023-08-14 op
356 b42d807f 2023-09-02 op static int
357 b42d807f 2023-09-02 op dispatch_event(const char *msg)
358 b42d807f 2023-09-02 op {
359 b42d807f 2023-09-02 op struct client *clt;
360 b42d807f 2023-09-02 op size_t len;
361 b42d807f 2023-09-02 op int ret = 0;
362 b42d807f 2023-09-02 op
363 b42d807f 2023-09-02 op len = strlen(msg);
364 b42d807f 2023-09-02 op TAILQ_FOREACH(clt, &clients, clients) {
365 b42d807f 2023-09-02 op if (!clt->ws || clt->done || clt->err)
366 b42d807f 2023-09-02 op continue;
367 b42d807f 2023-09-02 op
368 b42d807f 2023-09-02 op if (ws_compose(clt, WST_TEXT, msg, len) == -1)
369 b42d807f 2023-09-02 op ret = -1;
370 b42d807f 2023-09-02 op
371 6be81433 2024-04-14 op ev_add(clt->bio.fd, EV_READ|EV_WRITE, client_ev, clt);
372 b42d807f 2023-09-02 op }
373 b42d807f 2023-09-02 op
374 b42d807f 2023-09-02 op return (ret);
375 b42d807f 2023-09-02 op }
376 b42d807f 2023-09-02 op
377 b42d807f 2023-09-02 op static int
378 b42d807f 2023-09-02 op dispatch_event_status(void)
379 b42d807f 2023-09-02 op {
380 b42d807f 2023-09-02 op const char *status;
381 b42d807f 2023-09-02 op char buf[PATH_MAX + 2];
382 b42d807f 2023-09-02 op int r;
383 b42d807f 2023-09-02 op
384 b42d807f 2023-09-02 op switch (player_status.status) {
385 b42d807f 2023-09-02 op case STATE_STOPPED: status = "stopped"; break;
386 b42d807f 2023-09-02 op case STATE_PLAYING: status = "playing"; break;
387 b42d807f 2023-09-02 op case STATE_PAUSED: status = "paused"; break;
388 b42d807f 2023-09-02 op default: status = "unknown";
389 b42d807f 2023-09-02 op }
390 b42d807f 2023-09-02 op
391 b42d807f 2023-09-02 op r = snprintf(buf, sizeof(buf), "S:%s", status);
392 b42d807f 2023-09-02 op if (r < 0 || (size_t)r >= sizeof(buf)) {
393 b42d807f 2023-09-02 op log_warn("snprintf");
394 b42d807f 2023-09-02 op return -1;
395 b42d807f 2023-09-02 op }
396 b42d807f 2023-09-02 op dispatch_event(buf);
397 b42d807f 2023-09-02 op
398 b42d807f 2023-09-02 op r = snprintf(buf, sizeof(buf), "r:%s",
399 b42d807f 2023-09-02 op player_status.mode.repeat_one == MODE_ON ? "on" : "off");
400 b42d807f 2023-09-02 op if (r < 0 || (size_t)r >= sizeof(buf)) {
401 b42d807f 2023-09-02 op log_warn("snprintf");
402 b42d807f 2023-09-02 op return -1;
403 b42d807f 2023-09-02 op }
404 b42d807f 2023-09-02 op dispatch_event(buf);
405 b42d807f 2023-09-02 op
406 b42d807f 2023-09-02 op r = snprintf(buf, sizeof(buf), "R:%s",
407 b42d807f 2023-09-02 op player_status.mode.repeat_all == MODE_ON ? "on" : "off");
408 b42d807f 2023-09-02 op if (r < 0 || (size_t)r >= sizeof(buf)) {
409 b42d807f 2023-09-02 op log_warn("snprintf");
410 b42d807f 2023-09-02 op return -1;
411 b42d807f 2023-09-02 op }
412 b42d807f 2023-09-02 op dispatch_event(buf);
413 b42d807f 2023-09-02 op
414 b42d807f 2023-09-02 op r = snprintf(buf, sizeof(buf), "c:%s",
415 b42d807f 2023-09-02 op player_status.mode.consume == MODE_ON ? "on" : "off");
416 b42d807f 2023-09-02 op if (r < 0 || (size_t)r >= sizeof(buf)) {
417 b42d807f 2023-09-02 op log_warn("snprintf");
418 b42d807f 2023-09-02 op return -1;
419 b42d807f 2023-09-02 op }
420 b42d807f 2023-09-02 op dispatch_event(buf);
421 b42d807f 2023-09-02 op
422 b42d807f 2023-09-02 op r = snprintf(buf, sizeof(buf), "C:%s", player_status.path);
423 b42d807f 2023-09-02 op if (r < 0 || (size_t)r >= sizeof(buf)) {
424 b42d807f 2023-09-02 op log_warn("snprintf");
425 b42d807f 2023-09-02 op return -1;
426 b42d807f 2023-09-02 op }
427 b42d807f 2023-09-02 op dispatch_event(buf);
428 b42d807f 2023-09-02 op
429 b42d807f 2023-09-02 op return 0;
430 b42d807f 2023-09-02 op }
431 b42d807f 2023-09-02 op
432 b42d807f 2023-09-02 op static int
433 b42d807f 2023-09-02 op dispatch_event_track(struct player_status *ps)
434 b42d807f 2023-09-02 op {
435 b42d807f 2023-09-02 op char p[PATH_MAX + 2];
436 b42d807f 2023-09-02 op int r;
437 b42d807f 2023-09-02 op
438 b42d807f 2023-09-02 op r = snprintf(p, sizeof(p), "%c:%s",
439 b42d807f 2023-09-02 op ps->status == STATE_PLAYING ? 'A' : 'a', ps->path);
440 b42d807f 2023-09-02 op if (r < 0 || (size_t)r >= sizeof(p))
441 b42d807f 2023-09-02 op return (-1);
442 b42d807f 2023-09-02 op
443 b42d807f 2023-09-02 op return dispatch_event(p);
444 b42d807f 2023-09-02 op }
445 b42d807f 2023-09-02 op
446 04e4e993 2023-08-14 op static void
447 20dff2d2 2023-08-31 op imsg_dispatch(int fd, int ev, void *d)
448 b478a4bd 2023-08-14 op {
449 20dff2d2 2023-08-31 op static ssize_t off;
450 20dff2d2 2023-08-31 op static int off_found;
451 b42d807f 2023-09-02 op char seekmsg[128];
452 20dff2d2 2023-08-31 op struct imsg imsg;
453 9307af9e 2024-01-21 op struct ibuf ibuf;
454 20dff2d2 2023-08-31 op struct player_status ps;
455 20dff2d2 2023-08-31 op struct player_event event;
456 20dff2d2 2023-08-31 op const char *msg;
457 20dff2d2 2023-08-31 op ssize_t n;
458 20dff2d2 2023-08-31 op size_t datalen;
459 b42d807f 2023-09-02 op int r;
460 b478a4bd 2023-08-14 op
461 6be81433 2024-04-14 op if (ev & EV_READ) {
462 50e0da0e 2024-01-21 op if ((n = imsg_read(&imsgbuf)) == -1 && errno != EAGAIN)
463 20dff2d2 2023-08-31 op fatal("imsg_read");
464 20dff2d2 2023-08-31 op if (n == 0)
465 20dff2d2 2023-08-31 op fatalx("pipe closed");
466 b478a4bd 2023-08-14 op }
467 6be81433 2024-04-14 op if (ev & EV_WRITE) {
468 50e0da0e 2024-01-21 op if ((n = msgbuf_write(&imsgbuf.w)) == -1 && errno != EAGAIN)
469 20dff2d2 2023-08-31 op fatal("msgbuf_write");
470 20dff2d2 2023-08-31 op if (n == 0)
471 20dff2d2 2023-08-31 op fatalx("pipe closed");
472 20dff2d2 2023-08-31 op }
473 b478a4bd 2023-08-14 op
474 20dff2d2 2023-08-31 op for (;;) {
475 50e0da0e 2024-01-21 op if ((n = imsg_get(&imsgbuf, &imsg)) == -1)
476 20dff2d2 2023-08-31 op fatal("imsg_get");
477 20dff2d2 2023-08-31 op if (n == 0)
478 20dff2d2 2023-08-31 op break;
479 20dff2d2 2023-08-31 op
480 20dff2d2 2023-08-31 op datalen = IMSG_DATA_SIZE(imsg);
481 20dff2d2 2023-08-31 op
482 ddaffed6 2024-01-21 op switch (imsg_get_type(&imsg)) {
483 20dff2d2 2023-08-31 op case IMSG_CTL_ERR:
484 9307af9e 2024-01-21 op if (imsg_get_ibuf(&imsg, &ibuf) == -1 ||
485 9307af9e 2024-01-21 op (datalen = ibuf_size(&ibuf)) == 0 ||
486 9307af9e 2024-01-21 op (msg = ibuf_data(&ibuf)) == NULL ||
487 9307af9e 2024-01-21 op msg[datalen - 1] != '\0')
488 20dff2d2 2023-08-31 op fatalx("malformed error message");
489 20dff2d2 2023-08-31 op log_warnx("error: %s", msg);
490 20dff2d2 2023-08-31 op break;
491 20dff2d2 2023-08-31 op
492 20dff2d2 2023-08-31 op case IMSG_CTL_ADD:
493 20dff2d2 2023-08-31 op playlist_free(&playlist_tmp);
494 50e0da0e 2024-01-21 op imsg_compose(&imsgbuf, IMSG_CTL_SHOW, 0, 0, -1,
495 50e0da0e 2024-01-21 op NULL, 0);
496 20dff2d2 2023-08-31 op break;
497 20dff2d2 2023-08-31 op
498 20dff2d2 2023-08-31 op case IMSG_CTL_MONITOR:
499 9307af9e 2024-01-21 op if (imsg_get_data(&imsg, &event, sizeof(event)) == -1)
500 20dff2d2 2023-08-31 op fatalx("corrupted IMSG_CTL_MONITOR");
501 20dff2d2 2023-08-31 op switch (event.event) {
502 20dff2d2 2023-08-31 op case IMSG_CTL_PLAY:
503 20dff2d2 2023-08-31 op case IMSG_CTL_PAUSE:
504 20dff2d2 2023-08-31 op case IMSG_CTL_STOP:
505 20dff2d2 2023-08-31 op case IMSG_CTL_MODE:
506 50e0da0e 2024-01-21 op imsg_compose(&imsgbuf, IMSG_CTL_STATUS, 0, 0,
507 50e0da0e 2024-01-21 op -1, NULL, 0);
508 20dff2d2 2023-08-31 op break;
509 20dff2d2 2023-08-31 op
510 20dff2d2 2023-08-31 op case IMSG_CTL_NEXT:
511 20dff2d2 2023-08-31 op case IMSG_CTL_PREV:
512 20dff2d2 2023-08-31 op case IMSG_CTL_JUMP:
513 20dff2d2 2023-08-31 op case IMSG_CTL_COMMIT:
514 50e0da0e 2024-01-21 op imsg_compose(&imsgbuf, IMSG_CTL_SHOW, 0, 0, -1,
515 20dff2d2 2023-08-31 op NULL, 0);
516 50e0da0e 2024-01-21 op imsg_compose(&imsgbuf, IMSG_CTL_STATUS, 0, 0,
517 50e0da0e 2024-01-21 op -1, NULL, 0);
518 20dff2d2 2023-08-31 op break;
519 20dff2d2 2023-08-31 op
520 20dff2d2 2023-08-31 op case IMSG_CTL_SEEK:
521 20dff2d2 2023-08-31 op position = event.position;
522 20dff2d2 2023-08-31 op duration = event.duration;
523 b42d807f 2023-09-02 op r = snprintf(seekmsg, sizeof(seekmsg),
524 b42d807f 2023-09-02 op "s:%lld %lld", (long long)position,
525 b42d807f 2023-09-02 op (long long)duration);
526 b42d807f 2023-09-02 op if (r < 0 || (size_t)r >= sizeof(seekmsg)) {
527 b42d807f 2023-09-02 op log_warn("snprintf failed");
528 b42d807f 2023-09-02 op break;
529 b42d807f 2023-09-02 op }
530 b42d807f 2023-09-02 op dispatch_event(seekmsg);
531 20dff2d2 2023-08-31 op break;
532 20dff2d2 2023-08-31 op
533 20dff2d2 2023-08-31 op default:
534 20dff2d2 2023-08-31 op log_debug("ignoring event %d", event.event);
535 20dff2d2 2023-08-31 op break;
536 20dff2d2 2023-08-31 op }
537 20dff2d2 2023-08-31 op break;
538 20dff2d2 2023-08-31 op
539 20dff2d2 2023-08-31 op case IMSG_CTL_SHOW:
540 9307af9e 2024-01-21 op if (imsg_get_len(&imsg) == 0) {
541 b42d807f 2023-09-02 op if (playlist_tmp.len == 0) {
542 b42d807f 2023-09-02 op dispatch_event("x:");
543 b42d807f 2023-09-02 op off = -1;
544 03069bc3 2023-09-02 op } else if (playlist_tmp.len == off)
545 03069bc3 2023-09-02 op off = -1;
546 0e016c89 2023-09-07 op dispatch_event("X:");
547 20dff2d2 2023-08-31 op playlist_swap(&playlist_tmp, off);
548 20dff2d2 2023-08-31 op memset(&playlist_tmp, 0, sizeof(playlist_tmp));
549 20dff2d2 2023-08-31 op off = 0;
550 20dff2d2 2023-08-31 op off_found = 0;
551 20dff2d2 2023-08-31 op break;
552 20dff2d2 2023-08-31 op }
553 9307af9e 2024-01-21 op if (imsg_get_data(&imsg, &ps, sizeof(ps)) == -1)
554 20dff2d2 2023-08-31 op fatalx("corrupted IMSG_CTL_SHOW");
555 20dff2d2 2023-08-31 op if (ps.path[sizeof(ps.path) - 1] != '\0')
556 20dff2d2 2023-08-31 op fatalx("corrupted IMSG_CTL_SHOW");
557 b42d807f 2023-09-02 op if (playlist_tmp.len == 0)
558 b42d807f 2023-09-02 op dispatch_event("x:");
559 b42d807f 2023-09-02 op dispatch_event_track(&ps);
560 20dff2d2 2023-08-31 op playlist_push(&playlist_tmp, ps.path);
561 20dff2d2 2023-08-31 op if (ps.status == STATE_PLAYING)
562 20dff2d2 2023-08-31 op off_found = 1;
563 20dff2d2 2023-08-31 op if (!off_found)
564 20dff2d2 2023-08-31 op off++;
565 20dff2d2 2023-08-31 op break;
566 20dff2d2 2023-08-31 op
567 20dff2d2 2023-08-31 op case IMSG_CTL_STATUS:
568 9307af9e 2024-01-21 op if (imsg_get_data(&imsg, &player_status,
569 9307af9e 2024-01-21 op sizeof(player_status)) == -1)
570 20dff2d2 2023-08-31 op fatalx("corrupted IMSG_CTL_STATUS");
571 20dff2d2 2023-08-31 op if (player_status.path[sizeof(player_status.path) - 1]
572 20dff2d2 2023-08-31 op != '\0')
573 20dff2d2 2023-08-31 op fatalx("corrupted IMSG_CTL_STATUS");
574 b42d807f 2023-09-02 op dispatch_event_status();
575 20dff2d2 2023-08-31 op break;
576 20dff2d2 2023-08-31 op }
577 20dff2d2 2023-08-31 op }
578 20dff2d2 2023-08-31 op
579 6be81433 2024-04-14 op ev = EV_READ;
580 50e0da0e 2024-01-21 op if (imsgbuf.w.queued)
581 6be81433 2024-04-14 op ev |= EV_WRITE;
582 20dff2d2 2023-08-31 op ev_add(fd, ev, imsg_dispatch, NULL);
583 b478a4bd 2023-08-14 op }
584 b478a4bd 2023-08-14 op
585 b478a4bd 2023-08-14 op static void
586 3634fa70 2023-08-31 op route_notfound(struct client *clt)
587 04e4e993 2023-08-14 op {
588 3634fa70 2023-08-31 op if (http_reply(clt, 404, "Not Found", "text/plain") == -1 ||
589 3634fa70 2023-08-31 op http_writes(clt, "Page not found\n") == -1)
590 04e4e993 2023-08-14 op return;
591 04e4e993 2023-08-14 op }
592 04e4e993 2023-08-14 op
593 04e4e993 2023-08-14 op static void
594 3634fa70 2023-08-31 op render_playlist(struct client *clt)
595 04e4e993 2023-08-14 op {
596 20dff2d2 2023-08-31 op ssize_t i;
597 9ae51b1f 2023-09-02 op const char *path;
598 20dff2d2 2023-08-31 op int current;
599 04e4e993 2023-08-14 op
600 3634fa70 2023-08-31 op http_writes(clt, "<section class='playlist-wrapper'>");
601 3634fa70 2023-08-31 op http_writes(clt, "<form action=jump method=post"
602 04e4e993 2023-08-14 op " enctype='"FORM_URLENCODED"'>");
603 3634fa70 2023-08-31 op http_writes(clt, "<ul class=playlist>");
604 04e4e993 2023-08-14 op
605 20dff2d2 2023-08-31 op for (i = 0; i < playlist.len; ++i) {
606 20dff2d2 2023-08-31 op current = play_off == i;
607 04e4e993 2023-08-14 op
608 9ae51b1f 2023-09-02 op path = playlist.songs[i];
609 04e4e993 2023-08-14 op
610 20dff2d2 2023-08-31 op http_fmt(clt, "<li%s>", current ? " id=current" : "");
611 20dff2d2 2023-08-31 op http_writes(clt, "<button type=submit name=jump value=\"");
612 20dff2d2 2023-08-31 op http_htmlescape(clt, path);
613 20dff2d2 2023-08-31 op http_writes(clt, "\">");
614 9ae51b1f 2023-09-02 op http_htmlescape(clt, path);
615 20dff2d2 2023-08-31 op http_writes(clt, "</button></li>");
616 04e4e993 2023-08-14 op }
617 04e4e993 2023-08-14 op
618 3634fa70 2023-08-31 op http_writes(clt, "</ul>");
619 3634fa70 2023-08-31 op http_writes(clt, "</form>");
620 3634fa70 2023-08-31 op http_writes(clt, "</section>");
621 04e4e993 2023-08-14 op }
622 04e4e993 2023-08-14 op
623 04e4e993 2023-08-14 op static void
624 3634fa70 2023-08-31 op render_controls(struct client *clt)
625 04e4e993 2023-08-14 op {
626 04e4e993 2023-08-14 op const char *oc, *ac, *p;
627 04e4e993 2023-08-14 op int playing;
628 04e4e993 2023-08-14 op
629 20dff2d2 2023-08-31 op ac = player_status.mode.repeat_all ? " class='mode-active'" : "";
630 20dff2d2 2023-08-31 op oc = player_status.mode.repeat_one ? " class='mode-active'" : "";
631 20dff2d2 2023-08-31 op playing = player_status.status == STATE_PLAYING;
632 04e4e993 2023-08-14 op
633 20dff2d2 2023-08-31 op if ((p = strrchr(player_status.path, '/')) != NULL)
634 04e4e993 2023-08-14 op p++;
635 04e4e993 2023-08-14 op else
636 20dff2d2 2023-08-31 op p = player_status.path;
637 04e4e993 2023-08-14 op
638 3634fa70 2023-08-31 op if (http_writes(clt, "<section class=controls>") == -1 ||
639 3634fa70 2023-08-31 op http_writes(clt, "<p><a href='#current'>") == -1 ||
640 3634fa70 2023-08-31 op http_htmlescape(clt, p) == -1 ||
641 3634fa70 2023-08-31 op http_writes(clt, "</a></p>") == -1 ||
642 3634fa70 2023-08-31 op http_writes(clt, "<form action=ctrls method=post"
643 04e4e993 2023-08-14 op " enctype='"FORM_URLENCODED"'>") == -1 ||
644 3634fa70 2023-08-31 op http_writes(clt, "<button type=submit name=ctl value=prev>"
645 04e4e993 2023-08-14 op ICON_PREV"</button>") == -1 ||
646 b42d807f 2023-09-02 op http_fmt(clt, "<button id='toggle' type=submit name=ctl value=%s>"
647 04e4e993 2023-08-14 op "%s</button>", playing ? "pause" : "play",
648 04e4e993 2023-08-14 op playing ? ICON_PAUSE : ICON_PLAY) == -1 ||
649 3634fa70 2023-08-31 op http_writes(clt, "<button type=submit name=ctl value=next>"
650 04e4e993 2023-08-14 op ICON_NEXT"</button>") == -1 ||
651 3634fa70 2023-08-31 op http_writes(clt, "</form>") == -1 ||
652 3634fa70 2023-08-31 op http_writes(clt, "<form action=mode method=post"
653 3634fa70 2023-08-31 op " enctype='"FORM_URLENCODED"'>") == -1 ||
654 b42d807f 2023-09-02 op http_fmt(clt, "<button%s id=rall type=submit name=mode value=all>"
655 04e4e993 2023-08-14 op ICON_REPEAT_ALL"</button>", ac) == -1 ||
656 b42d807f 2023-09-02 op http_fmt(clt, "<button%s id=rone type=submit name=mode value=one>"
657 04e4e993 2023-08-14 op ICON_REPEAT_ONE"</button>", oc) == -1 ||
658 3634fa70 2023-08-31 op http_writes(clt, "</form>") == -1 ||
659 3634fa70 2023-08-31 op http_writes(clt, "</section>") == -1)
660 04e4e993 2023-08-14 op return;
661 04e4e993 2023-08-14 op }
662 04e4e993 2023-08-14 op
663 04e4e993 2023-08-14 op static void
664 3634fa70 2023-08-31 op route_home(struct client *clt)
665 04e4e993 2023-08-14 op {
666 3634fa70 2023-08-31 op if (http_reply(clt, 200, "OK", "text/html;charset=UTF-8") == -1)
667 04e4e993 2023-08-14 op return;
668 04e4e993 2023-08-14 op
669 3634fa70 2023-08-31 op if (http_write(clt, head, strlen(head)) == -1)
670 04e4e993 2023-08-14 op return;
671 04e4e993 2023-08-14 op
672 3634fa70 2023-08-31 op if (http_writes(clt, "<main>") == -1)
673 04e4e993 2023-08-14 op return;
674 04e4e993 2023-08-14 op
675 3634fa70 2023-08-31 op render_controls(clt);
676 3634fa70 2023-08-31 op render_playlist(clt);
677 04e4e993 2023-08-14 op
678 3634fa70 2023-08-31 op if (http_writes(clt, "</main>") == -1)
679 04e4e993 2023-08-14 op return;
680 04e4e993 2023-08-14 op
681 3634fa70 2023-08-31 op http_write(clt, foot, strlen(foot));
682 04e4e993 2023-08-14 op }
683 04e4e993 2023-08-14 op
684 04e4e993 2023-08-14 op static void
685 3634fa70 2023-08-31 op route_jump(struct client *clt)
686 04e4e993 2023-08-14 op {
687 04e4e993 2023-08-14 op char path[PATH_MAX];
688 04e4e993 2023-08-14 op char *form, *field;
689 04e4e993 2023-08-14 op int found = 0;
690 04e4e993 2023-08-14 op
691 2216d3fb 2023-09-07 op http_postdata(clt, &form, NULL);
692 04e4e993 2023-08-14 op while ((field = strsep(&form, "&")) != NULL) {
693 04e4e993 2023-08-14 op if (url_decode(field) == -1)
694 04e4e993 2023-08-14 op goto badreq;
695 04e4e993 2023-08-14 op
696 04e4e993 2023-08-14 op if (strncmp(field, "jump=", 5) != 0)
697 04e4e993 2023-08-14 op continue;
698 04e4e993 2023-08-14 op field += 5;
699 04e4e993 2023-08-14 op found = 1;
700 04e4e993 2023-08-14 op
701 99b28969 2023-08-14 op memset(&path, 0, sizeof(path));
702 04e4e993 2023-08-14 op if (strlcpy(path, field, sizeof(path)) >= sizeof(path))
703 04e4e993 2023-08-14 op goto badreq;
704 04e4e993 2023-08-14 op
705 50e0da0e 2024-01-21 op imsg_compose(&imsgbuf, IMSG_CTL_JUMP, 0, 0, -1,
706 04e4e993 2023-08-14 op path, sizeof(path));
707 6be81433 2024-04-14 op ev_add(imsgbuf.w.fd, EV_READ|EV_WRITE, imsg_dispatch, NULL);
708 04e4e993 2023-08-14 op break;
709 04e4e993 2023-08-14 op }
710 04e4e993 2023-08-14 op
711 04e4e993 2023-08-14 op if (!found)
712 04e4e993 2023-08-14 op goto badreq;
713 04e4e993 2023-08-14 op
714 b42d807f 2023-09-02 op if (!strncmp(clt->req.path, "/a/", 2))
715 b42d807f 2023-09-02 op http_reply(clt, 200, "OK", "text/plain");
716 b42d807f 2023-09-02 op else
717 b42d807f 2023-09-02 op http_reply(clt, 302, "See Other", "/");
718 04e4e993 2023-08-14 op return;
719 04e4e993 2023-08-14 op
720 04e4e993 2023-08-14 op badreq:
721 3634fa70 2023-08-31 op http_reply(clt, 400, "Bad Request", "text/plain");
722 3634fa70 2023-08-31 op http_writes(clt, "Bad Request.\n");
723 04e4e993 2023-08-14 op }
724 04e4e993 2023-08-14 op
725 04e4e993 2023-08-14 op static void
726 3634fa70 2023-08-31 op route_controls(struct client *clt)
727 04e4e993 2023-08-14 op {
728 04e4e993 2023-08-14 op char *form, *field;
729 04e4e993 2023-08-14 op int cmd, found = 0;
730 04e4e993 2023-08-14 op
731 2216d3fb 2023-09-07 op http_postdata(clt, &form, NULL);
732 04e4e993 2023-08-14 op while ((field = strsep(&form, "&")) != NULL) {
733 04e4e993 2023-08-14 op if (url_decode(field) == -1)
734 04e4e993 2023-08-14 op goto badreq;
735 04e4e993 2023-08-14 op
736 04e4e993 2023-08-14 op if (strncmp(field, "ctl=", 4) != 0)
737 04e4e993 2023-08-14 op continue;
738 04e4e993 2023-08-14 op field += 4;
739 04e4e993 2023-08-14 op found = 1;
740 04e4e993 2023-08-14 op
741 04e4e993 2023-08-14 op if (!strcmp(field, "play"))
742 04e4e993 2023-08-14 op cmd = IMSG_CTL_PLAY;
743 04e4e993 2023-08-14 op else if (!strcmp(field, "pause"))
744 04e4e993 2023-08-14 op cmd = IMSG_CTL_PAUSE;
745 04e4e993 2023-08-14 op else if (!strcmp(field, "next"))
746 04e4e993 2023-08-14 op cmd = IMSG_CTL_NEXT;
747 04e4e993 2023-08-14 op else if (!strcmp(field, "prev"))
748 04e4e993 2023-08-14 op cmd = IMSG_CTL_PREV;
749 04e4e993 2023-08-14 op else
750 04e4e993 2023-08-14 op goto badreq;
751 04e4e993 2023-08-14 op
752 50e0da0e 2024-01-21 op imsg_compose(&imsgbuf, cmd, 0, 0, -1, NULL, 0);
753 50e0da0e 2024-01-21 op imsg_flush(&imsgbuf);
754 04e4e993 2023-08-14 op break;
755 04e4e993 2023-08-14 op }
756 04e4e993 2023-08-14 op
757 04e4e993 2023-08-14 op if (!found)
758 04e4e993 2023-08-14 op goto badreq;
759 04e4e993 2023-08-14 op
760 b42d807f 2023-09-02 op if (!strncmp(clt->req.path, "/a/", 2))
761 b42d807f 2023-09-02 op http_reply(clt, 200, "OK", "text/plain");
762 b42d807f 2023-09-02 op else
763 b42d807f 2023-09-02 op http_reply(clt, 302, "See Other", "/");
764 04e4e993 2023-08-14 op return;
765 04e4e993 2023-08-14 op
766 04e4e993 2023-08-14 op badreq:
767 3634fa70 2023-08-31 op http_reply(clt, 400, "Bad Request", "text/plain");
768 3634fa70 2023-08-31 op http_writes(clt, "Bad Request.\n");
769 04e4e993 2023-08-14 op }
770 04e4e993 2023-08-14 op
771 04e4e993 2023-08-14 op static void
772 3634fa70 2023-08-31 op route_mode(struct client *clt)
773 04e4e993 2023-08-14 op {
774 04e4e993 2023-08-14 op char *form, *field;
775 04e4e993 2023-08-14 op int found = 0;
776 04e4e993 2023-08-14 op struct player_mode pm;
777 04e4e993 2023-08-14 op
778 04e4e993 2023-08-14 op pm.repeat_one = pm.repeat_all = pm.consume = MODE_UNDEF;
779 04e4e993 2023-08-14 op
780 2216d3fb 2023-09-07 op http_postdata(clt, &form, NULL);
781 04e4e993 2023-08-14 op while ((field = strsep(&form, "&")) != NULL) {
782 04e4e993 2023-08-14 op if (url_decode(field) == -1)
783 04e4e993 2023-08-14 op goto badreq;
784 04e4e993 2023-08-14 op
785 04e4e993 2023-08-14 op if (strncmp(field, "mode=", 5) != 0)
786 04e4e993 2023-08-14 op continue;
787 04e4e993 2023-08-14 op field += 5;
788 04e4e993 2023-08-14 op found = 1;
789 04e4e993 2023-08-14 op
790 04e4e993 2023-08-14 op if (!strcmp(field, "all"))
791 04e4e993 2023-08-14 op pm.repeat_all = MODE_TOGGLE;
792 04e4e993 2023-08-14 op else if (!strcmp(field, "one"))
793 04e4e993 2023-08-14 op pm.repeat_one = MODE_TOGGLE;
794 04e4e993 2023-08-14 op else
795 04e4e993 2023-08-14 op goto badreq;
796 04e4e993 2023-08-14 op
797 50e0da0e 2024-01-21 op imsg_compose(&imsgbuf, IMSG_CTL_MODE, 0, 0, -1,
798 50e0da0e 2024-01-21 op &pm, sizeof(pm));
799 6be81433 2024-04-14 op ev_add(imsgbuf.w.fd, EV_READ|EV_WRITE, imsg_dispatch, NULL);
800 04e4e993 2023-08-14 op break;
801 04e4e993 2023-08-14 op }
802 04e4e993 2023-08-14 op
803 04e4e993 2023-08-14 op if (!found)
804 04e4e993 2023-08-14 op goto badreq;
805 04e4e993 2023-08-14 op
806 b42d807f 2023-09-02 op if (!strncmp(clt->req.path, "/a/", 2))
807 b42d807f 2023-09-02 op http_reply(clt, 200, "OK", "text/plain");
808 b42d807f 2023-09-02 op else
809 b42d807f 2023-09-02 op http_reply(clt, 302, "See Other", "/");
810 04e4e993 2023-08-14 op return;
811 04e4e993 2023-08-14 op
812 04e4e993 2023-08-14 op badreq:
813 3634fa70 2023-08-31 op http_reply(clt, 400, "Bad Request", "text/plain");
814 3634fa70 2023-08-31 op http_writes(clt, "Bad Request.\n");
815 b42d807f 2023-09-02 op }
816 b42d807f 2023-09-02 op
817 b42d807f 2023-09-02 op static void
818 b42d807f 2023-09-02 op route_handle_ws(struct client *clt)
819 b42d807f 2023-09-02 op {
820 6be81433 2024-04-14 op struct buf *rbuf = &clt->bio.rbuf;
821 b42d807f 2023-09-02 op int type;
822 b42d807f 2023-09-02 op size_t len;
823 b42d807f 2023-09-02 op
824 b42d807f 2023-09-02 op if (ws_read(clt, &type, &len) == -1) {
825 b42d807f 2023-09-02 op if (errno != EAGAIN) {
826 b42d807f 2023-09-02 op log_warn("ws_read");
827 b42d807f 2023-09-02 op clt->done = 1;
828 b42d807f 2023-09-02 op }
829 b42d807f 2023-09-02 op return;
830 b42d807f 2023-09-02 op }
831 b42d807f 2023-09-02 op
832 b42d807f 2023-09-02 op switch (type) {
833 b42d807f 2023-09-02 op case WST_PING:
834 b42d807f 2023-09-02 op ws_compose(clt, WST_PONG, rbuf->buf, len);
835 b42d807f 2023-09-02 op break;
836 b42d807f 2023-09-02 op case WST_TEXT:
837 b42d807f 2023-09-02 op /* log_info("<<< %.*s", (int)len, rbuf->buf); */
838 b42d807f 2023-09-02 op break;
839 b42d807f 2023-09-02 op case WST_CLOSE:
840 b42d807f 2023-09-02 op /* TODO send a close too (ack) */
841 b42d807f 2023-09-02 op clt->done = 1;
842 b42d807f 2023-09-02 op break;
843 b42d807f 2023-09-02 op default:
844 b42d807f 2023-09-02 op log_info("got unexpected ws frame type 0x%02x", type);
845 b42d807f 2023-09-02 op break;
846 b42d807f 2023-09-02 op }
847 b42d807f 2023-09-02 op
848 b42d807f 2023-09-02 op buf_drain(rbuf, len);
849 b42d807f 2023-09-02 op }
850 b42d807f 2023-09-02 op
851 b42d807f 2023-09-02 op static void
852 b42d807f 2023-09-02 op route_init_ws(struct client *clt)
853 b42d807f 2023-09-02 op {
854 b42d807f 2023-09-02 op if (!(clt->req.flags & (R_CONNUPGR|R_UPGRADEWS|R_WSVERSION)) ||
855 b42d807f 2023-09-02 op clt->req.secret == NULL) {
856 b42d807f 2023-09-02 op http_reply(clt, 400, "Bad Request", "text/plain");
857 b42d807f 2023-09-02 op http_writes(clt, "Invalid websocket handshake.\r\n");
858 b42d807f 2023-09-02 op return;
859 b42d807f 2023-09-02 op }
860 b42d807f 2023-09-02 op
861 b42d807f 2023-09-02 op clt->ws = 1;
862 b42d807f 2023-09-02 op clt->done = 0;
863 b42d807f 2023-09-02 op clt->route = route_handle_ws;
864 b42d807f 2023-09-02 op http_reply(clt, 101, "Switching Protocols", NULL);
865 04e4e993 2023-08-14 op }
866 04e4e993 2023-08-14 op
867 04e4e993 2023-08-14 op static void
868 cc932200 2023-08-31 op route_assets(struct client *clt)
869 cc932200 2023-08-31 op {
870 cc932200 2023-08-31 op if (!strcmp(clt->req.path, "/style.css")) {
871 cc932200 2023-08-31 op http_reply(clt, 200, "OK", "text/css");
872 cc932200 2023-08-31 op http_write(clt, css, strlen(css));
873 cc932200 2023-08-31 op return;
874 cc932200 2023-08-31 op }
875 cc932200 2023-08-31 op
876 cc932200 2023-08-31 op if (!strcmp(clt->req.path, "/app.js")) {
877 cc932200 2023-08-31 op http_reply(clt, 200, "OK", "application/javascript");
878 cc932200 2023-08-31 op http_write(clt, js, strlen(js));
879 cc932200 2023-08-31 op return;
880 cc932200 2023-08-31 op }
881 cc932200 2023-08-31 op
882 cc932200 2023-08-31 op route_notfound(clt);
883 cc932200 2023-08-31 op }
884 cc932200 2023-08-31 op
885 cc932200 2023-08-31 op static void
886 3634fa70 2023-08-31 op route_dispatch(struct client *clt)
887 04e4e993 2023-08-14 op {
888 04e4e993 2023-08-14 op static const struct route {
889 3634fa70 2023-08-31 op int method;
890 3634fa70 2023-08-31 op const char *path;
891 3634fa70 2023-08-31 op route_fn route;
892 04e4e993 2023-08-14 op } routes[] = {
893 04e4e993 2023-08-14 op { METHOD_GET, "/", &route_home },
894 b42d807f 2023-09-02 op
895 04e4e993 2023-08-14 op { METHOD_POST, "/jump", &route_jump },
896 04e4e993 2023-08-14 op { METHOD_POST, "/ctrls", &route_controls },
897 04e4e993 2023-08-14 op { METHOD_POST, "/mode", &route_mode },
898 04e4e993 2023-08-14 op
899 b42d807f 2023-09-02 op { METHOD_POST, "/a/jump", &route_jump },
900 b42d807f 2023-09-02 op { METHOD_POST, "/a/ctrls", &route_controls },
901 b42d807f 2023-09-02 op { METHOD_POST, "/a/mode", &route_mode },
902 b42d807f 2023-09-02 op
903 b42d807f 2023-09-02 op { METHOD_GET, "/ws", &route_init_ws },
904 b42d807f 2023-09-02 op
905 cc932200 2023-08-31 op { METHOD_GET, "/style.css", &route_assets },
906 cc932200 2023-08-31 op { METHOD_GET, "/app.js", &route_assets },
907 cc932200 2023-08-31 op
908 04e4e993 2023-08-14 op { METHOD_GET, "*", &route_notfound },
909 04e4e993 2023-08-14 op { METHOD_POST, "*", &route_notfound },
910 04e4e993 2023-08-14 op };
911 3634fa70 2023-08-31 op struct request *req = &clt->req;
912 04e4e993 2023-08-14 op size_t i;
913 04e4e993 2023-08-14 op
914 04e4e993 2023-08-14 op if ((req->method != METHOD_GET && req->method != METHOD_POST) ||
915 04e4e993 2023-08-14 op (req->ctype != NULL && strcmp(req->ctype, FORM_URLENCODED) != 0) ||
916 04e4e993 2023-08-14 op req->path == NULL) {
917 3634fa70 2023-08-31 op http_reply(clt, 400, "Bad Request", NULL);
918 04e4e993 2023-08-14 op return;
919 04e4e993 2023-08-14 op }
920 04e4e993 2023-08-14 op
921 04e4e993 2023-08-14 op for (i = 0; i < nitems(routes); ++i) {
922 04e4e993 2023-08-14 op if (req->method != routes[i].method ||
923 04e4e993 2023-08-14 op fnmatch(routes[i].path, req->path, 0) != 0)
924 04e4e993 2023-08-14 op continue;
925 3634fa70 2023-08-31 op clt->done = 1; /* assume with one round is done */
926 3634fa70 2023-08-31 op clt->route = routes[i].route;
927 3634fa70 2023-08-31 op clt->route(clt);
928 3634fa70 2023-08-31 op if (clt->done)
929 3634fa70 2023-08-31 op http_close(clt);
930 04e4e993 2023-08-14 op return;
931 04e4e993 2023-08-14 op }
932 04e4e993 2023-08-14 op }
933 04e4e993 2023-08-14 op
934 04e4e993 2023-08-14 op static void
935 3634fa70 2023-08-31 op client_ev(int fd, int ev, void *d)
936 3634fa70 2023-08-31 op {
937 3634fa70 2023-08-31 op struct client *clt = d;
938 917841c0 2024-04-14 op ssize_t ret;
939 3634fa70 2023-08-31 op
940 6be81433 2024-04-14 op if (ev & EV_READ) {
941 917841c0 2024-04-14 op if ((ret = bufio_read(&clt->bio)) == -1 && errno != EAGAIN) {
942 3634fa70 2023-08-31 op log_warn("bufio_read");
943 3634fa70 2023-08-31 op goto err;
944 3634fa70 2023-08-31 op }
945 917841c0 2024-04-14 op if (ret == 0)
946 917841c0 2024-04-14 op goto err;
947 3634fa70 2023-08-31 op }
948 3634fa70 2023-08-31 op
949 6be81433 2024-04-14 op if (ev & EV_WRITE) {
950 917841c0 2024-04-14 op if ((ret = bufio_write(&clt->bio)) == -1 && errno != EAGAIN) {
951 3d47cf24 2023-09-11 op log_warn("bufio_write");
952 3634fa70 2023-08-31 op goto err;
953 3634fa70 2023-08-31 op }
954 917841c0 2024-04-14 op if (ret == 0)
955 917841c0 2024-04-14 op goto err;
956 3634fa70 2023-08-31 op }
957 3634fa70 2023-08-31 op
958 3634fa70 2023-08-31 op if (clt->route == NULL) {
959 3634fa70 2023-08-31 op if (http_parse(clt) == -1) {
960 3634fa70 2023-08-31 op if (errno == EAGAIN)
961 3634fa70 2023-08-31 op goto again;
962 3634fa70 2023-08-31 op log_warnx("HTTP parse request failed");
963 3634fa70 2023-08-31 op goto err;
964 3634fa70 2023-08-31 op }
965 3634fa70 2023-08-31 op if (clt->req.method == METHOD_POST &&
966 3634fa70 2023-08-31 op http_read(clt) == -1) {
967 3634fa70 2023-08-31 op if (errno == EAGAIN)
968 3634fa70 2023-08-31 op goto again;
969 3634fa70 2023-08-31 op log_warnx("failed to read POST data");
970 3634fa70 2023-08-31 op goto err;
971 3634fa70 2023-08-31 op }
972 3634fa70 2023-08-31 op route_dispatch(clt);
973 3634fa70 2023-08-31 op goto again;
974 3634fa70 2023-08-31 op }
975 3634fa70 2023-08-31 op
976 6d777267 2023-09-02 op if (!clt->done && !clt->err)
977 3634fa70 2023-08-31 op clt->route(clt);
978 3634fa70 2023-08-31 op
979 3634fa70 2023-08-31 op again:
980 6be81433 2024-04-14 op ev = bio_ev(&clt->bio);
981 6be81433 2024-04-14 op if (ev == EV_READ && (clt->done || clt->err)) {
982 3634fa70 2023-08-31 op goto err; /* done with this client */
983 3634fa70 2023-08-31 op }
984 3634fa70 2023-08-31 op
985 3634fa70 2023-08-31 op ev_add(fd, ev, client_ev, clt);
986 3634fa70 2023-08-31 op return;
987 3634fa70 2023-08-31 op
988 3634fa70 2023-08-31 op err:
989 3634fa70 2023-08-31 op ev_del(fd);
990 b42d807f 2023-09-02 op TAILQ_REMOVE(&clients, clt, clients);
991 3634fa70 2023-08-31 op http_free(clt);
992 3634fa70 2023-08-31 op }
993 3634fa70 2023-08-31 op
994 3634fa70 2023-08-31 op static void
995 703c260b 2023-08-25 op web_accept(int psock, int ev, void *d)
996 04e4e993 2023-08-14 op {
997 3634fa70 2023-08-31 op struct client *clt;
998 077802c1 2023-08-25 op int sock;
999 04e4e993 2023-08-14 op
1000 04e4e993 2023-08-14 op if ((sock = accept(psock, NULL, NULL)) == -1) {
1001 04e4e993 2023-08-14 op warn("accept");
1002 04e4e993 2023-08-14 op return;
1003 04e4e993 2023-08-14 op }
1004 3634fa70 2023-08-31 op if ((clt = calloc(1, sizeof(*clt))) == NULL ||
1005 3634fa70 2023-08-31 op http_init(clt, sock) == -1) {
1006 3634fa70 2023-08-31 op log_warn("failed to initialize client");
1007 3634fa70 2023-08-31 op free(clt);
1008 04e4e993 2023-08-14 op close(sock);
1009 04e4e993 2023-08-14 op return;
1010 04e4e993 2023-08-14 op }
1011 3634fa70 2023-08-31 op
1012 b42d807f 2023-09-02 op TAILQ_INSERT_TAIL(&clients, clt, clients);
1013 b42d807f 2023-09-02 op
1014 6be81433 2024-04-14 op client_ev(sock, EV_READ, clt);
1015 04e4e993 2023-08-14 op return;
1016 04e4e993 2023-08-14 op }
1017 04e4e993 2023-08-14 op
1018 04e4e993 2023-08-14 op void __dead
1019 04e4e993 2023-08-14 op usage(void)
1020 04e4e993 2023-08-14 op {
1021 9ae51b1f 2023-09-02 op fprintf(stderr, "usage: %s [-v] [-s sock] [[host] port]\n",
1022 04e4e993 2023-08-14 op getprogname());
1023 04e4e993 2023-08-14 op exit(1);
1024 04e4e993 2023-08-14 op }
1025 04e4e993 2023-08-14 op
1026 04e4e993 2023-08-14 op int
1027 04e4e993 2023-08-14 op main(int argc, char **argv)
1028 04e4e993 2023-08-14 op {
1029 04e4e993 2023-08-14 op struct addrinfo hints, *res, *res0;
1030 04e4e993 2023-08-14 op const char *cause = NULL;
1031 04e4e993 2023-08-14 op const char *host = NULL;
1032 04e4e993 2023-08-14 op const char *port = "9090";
1033 04e4e993 2023-08-14 op char *sock = NULL;
1034 80d5f5ad 2023-08-25 op size_t nsock, error, save_errno;
1035 80d5f5ad 2023-08-25 op int ch, v, amused_sock, fd;
1036 04e4e993 2023-08-14 op int verbose = 0;
1037 04e4e993 2023-08-14 op
1038 b42d807f 2023-09-02 op TAILQ_INIT(&clients);
1039 04e4e993 2023-08-14 op setlocale(LC_ALL, NULL);
1040 04e4e993 2023-08-14 op
1041 04e4e993 2023-08-14 op log_init(1, LOG_DAEMON);
1042 04e4e993 2023-08-14 op
1043 04e4e993 2023-08-14 op if (pledge("stdio rpath unix inet dns", NULL) == -1)
1044 04e4e993 2023-08-14 op err(1, "pledge");
1045 04e4e993 2023-08-14 op
1046 9ae51b1f 2023-09-02 op while ((ch = getopt(argc, argv, "s:v")) != -1) {
1047 04e4e993 2023-08-14 op switch (ch) {
1048 04e4e993 2023-08-14 op case 's':
1049 04e4e993 2023-08-14 op sock = optarg;
1050 04e4e993 2023-08-14 op break;
1051 04e4e993 2023-08-14 op case 'v':
1052 04e4e993 2023-08-14 op verbose = 1;
1053 04e4e993 2023-08-14 op break;
1054 04e4e993 2023-08-14 op default:
1055 04e4e993 2023-08-14 op usage();
1056 04e4e993 2023-08-14 op }
1057 04e4e993 2023-08-14 op }
1058 04e4e993 2023-08-14 op argc -= optind;
1059 04e4e993 2023-08-14 op argv += optind;
1060 04e4e993 2023-08-14 op
1061 04e4e993 2023-08-14 op if (argc == 1)
1062 04e4e993 2023-08-14 op port = argv[0];
1063 04e4e993 2023-08-14 op if (argc == 2) {
1064 04e4e993 2023-08-14 op host = argv[0];
1065 04e4e993 2023-08-14 op port = argv[1];
1066 04e4e993 2023-08-14 op }
1067 04e4e993 2023-08-14 op if (argc > 2)
1068 04e4e993 2023-08-14 op usage();
1069 04e4e993 2023-08-14 op
1070 04e4e993 2023-08-14 op log_setverbose(verbose);
1071 04e4e993 2023-08-14 op
1072 83f1c895 2023-12-10 op if (sock == NULL) {
1073 83f1c895 2023-12-10 op const char *tmpdir;
1074 83f1c895 2023-12-10 op
1075 83f1c895 2023-12-10 op if ((tmpdir = getenv("TMPDIR")) == NULL)
1076 83f1c895 2023-12-10 op tmpdir = "/tmp";
1077 04e4e993 2023-08-14 op
1078 83f1c895 2023-12-10 op xasprintf(&sock, "%s/amused-%d", tmpdir, getuid());
1079 83f1c895 2023-12-10 op }
1080 83f1c895 2023-12-10 op
1081 04e4e993 2023-08-14 op signal(SIGPIPE, SIG_IGN);
1082 04e4e993 2023-08-14 op
1083 80d5f5ad 2023-08-25 op if (ev_init() == -1)
1084 80d5f5ad 2023-08-25 op fatal("ev_init");
1085 80d5f5ad 2023-08-25 op
1086 04e4e993 2023-08-14 op amused_sock = dial(sock);
1087 50e0da0e 2024-01-21 op imsg_init(&imsgbuf, amused_sock);
1088 50e0da0e 2024-01-21 op imsg_compose(&imsgbuf, IMSG_CTL_SHOW, 0, 0, -1, NULL, 0);
1089 50e0da0e 2024-01-21 op imsg_compose(&imsgbuf, IMSG_CTL_STATUS, 0, 0, -1, NULL, 0);
1090 50e0da0e 2024-01-21 op imsg_compose(&imsgbuf, IMSG_CTL_MONITOR, 0, 0, -1, NULL, 0);
1091 6be81433 2024-04-14 op ev_add(amused_sock, EV_READ|EV_WRITE, imsg_dispatch, NULL);
1092 04e4e993 2023-08-14 op
1093 04e4e993 2023-08-14 op memset(&hints, 0, sizeof(hints));
1094 04e4e993 2023-08-14 op hints.ai_family = AF_UNSPEC;
1095 04e4e993 2023-08-14 op hints.ai_socktype = SOCK_STREAM;
1096 04e4e993 2023-08-14 op hints.ai_flags = AI_PASSIVE;
1097 04e4e993 2023-08-14 op error = getaddrinfo(host, port, &hints, &res0);
1098 04e4e993 2023-08-14 op if (error)
1099 04e4e993 2023-08-14 op errx(1, "%s", gai_strerror(error));
1100 04e4e993 2023-08-14 op
1101 04e4e993 2023-08-14 op nsock = 0;
1102 80d5f5ad 2023-08-25 op for (res = res0; res; res = res->ai_next) {
1103 80d5f5ad 2023-08-25 op fd = socket(res->ai_family, res->ai_socktype,
1104 04e4e993 2023-08-14 op res->ai_protocol);
1105 80d5f5ad 2023-08-25 op if (fd == -1) {
1106 04e4e993 2023-08-14 op cause = "socket";
1107 04e4e993 2023-08-14 op continue;
1108 04e4e993 2023-08-14 op }
1109 04e4e993 2023-08-14 op
1110 04e4e993 2023-08-14 op v = 1;
1111 80d5f5ad 2023-08-25 op if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
1112 04e4e993 2023-08-14 op &v, sizeof(v)) == -1)
1113 04e4e993 2023-08-14 op fatal("setsockopt(SO_REUSEADDR)");
1114 04e4e993 2023-08-14 op
1115 80d5f5ad 2023-08-25 op if (bind(fd, res->ai_addr, res->ai_addrlen) == -1) {
1116 04e4e993 2023-08-14 op cause = "bind";
1117 04e4e993 2023-08-14 op save_errno = errno;
1118 80d5f5ad 2023-08-25 op close(fd);
1119 04e4e993 2023-08-14 op errno = save_errno;
1120 04e4e993 2023-08-14 op continue;
1121 04e4e993 2023-08-14 op }
1122 04e4e993 2023-08-14 op
1123 80d5f5ad 2023-08-25 op if (listen(fd, 5) == -1)
1124 04e4e993 2023-08-14 op err(1, "listen");
1125 04e4e993 2023-08-14 op
1126 6be81433 2024-04-14 op if (ev_add(fd, EV_READ, web_accept, NULL) == -1)
1127 80d5f5ad 2023-08-25 op fatal("ev_add");
1128 04e4e993 2023-08-14 op nsock++;
1129 04e4e993 2023-08-14 op }
1130 04e4e993 2023-08-14 op if (nsock == 0)
1131 04e4e993 2023-08-14 op err(1, "%s", cause);
1132 04e4e993 2023-08-14 op freeaddrinfo(res0);
1133 04e4e993 2023-08-14 op
1134 04e4e993 2023-08-14 op if (pledge("stdio inet", NULL) == -1)
1135 04e4e993 2023-08-14 op err(1, "pledge");
1136 04e4e993 2023-08-14 op
1137 6e0ebd7a 2023-09-09 op log_info("listening on port %s", port);
1138 80d5f5ad 2023-08-25 op ev_loop();
1139 80d5f5ad 2023-08-25 op return (1);
1140 04e4e993 2023-08-14 op }