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 <poll.h>
31 04e4e993 2023-08-14 op #include <signal.h>
32 04e4e993 2023-08-14 op #include <stdio.h>
33 04e4e993 2023-08-14 op #include <stdlib.h>
34 04e4e993 2023-08-14 op #include <string.h>
35 04e4e993 2023-08-14 op #include <syslog.h>
36 04e4e993 2023-08-14 op #include <unistd.h>
37 04e4e993 2023-08-14 op
38 04e4e993 2023-08-14 op #include "amused.h"
39 3634fa70 2023-08-31 op #include "bufio.h"
40 80d5f5ad 2023-08-25 op #include "ev.h"
41 04e4e993 2023-08-14 op #include "http.h"
42 04e4e993 2023-08-14 op #include "log.h"
43 04e4e993 2023-08-14 op #include "playlist.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 04e4e993 2023-08-14 op static struct imsgbuf ibuf;
62 04e4e993 2023-08-14 op static const char *prefix = "";
63 04e4e993 2023-08-14 op static size_t prefixlen;
64 04e4e993 2023-08-14 op
65 04e4e993 2023-08-14 op const char *head = "<!doctype html>"
66 04e4e993 2023-08-14 op "<html>"
67 04e4e993 2023-08-14 op "<head>"
68 04e4e993 2023-08-14 op "<meta name='viewport' content='width=device-width, initial-scale=1'/>"
69 04e4e993 2023-08-14 op "<title>Amused Web</title>"
70 04e4e993 2023-08-14 op "<style>"
71 04e4e993 2023-08-14 op "*{box-sizing:border-box}"
72 04e4e993 2023-08-14 op "html,body{"
73 04e4e993 2023-08-14 op " padding: 0;"
74 04e4e993 2023-08-14 op " border: 0;"
75 04e4e993 2023-08-14 op " margin: 0;"
76 04e4e993 2023-08-14 op "}"
77 04e4e993 2023-08-14 op "main{"
78 04e4e993 2023-08-14 op " display: flex;"
79 04e4e993 2023-08-14 op " flex-direction: column;"
80 04e4e993 2023-08-14 op "}"
81 04e4e993 2023-08-14 op "button{cursor:pointer}"
82 04e4e993 2023-08-14 op ".searchbox{"
83 04e4e993 2023-08-14 op " position: sticky;"
84 04e4e993 2023-08-14 op " top: 0;"
85 04e4e993 2023-08-14 op "}"
86 04e4e993 2023-08-14 op ".searchbox input{"
87 04e4e993 2023-08-14 op " width: 100%;"
88 04e4e993 2023-08-14 op " padding: 9px;"
89 04e4e993 2023-08-14 op "}"
90 04e4e993 2023-08-14 op ".playlist-wrapper{min-height:80vh}"
91 04e4e993 2023-08-14 op ".playlist{"
92 04e4e993 2023-08-14 op " list-style: none;"
93 04e4e993 2023-08-14 op " padding: 0;"
94 04e4e993 2023-08-14 op " margin: 0;"
95 04e4e993 2023-08-14 op "}"
96 04e4e993 2023-08-14 op ".playlist button{"
97 04e4e993 2023-08-14 op " font-family: monospace;"
98 04e4e993 2023-08-14 op " text-align: left;"
99 04e4e993 2023-08-14 op " width: 100%;"
100 04e4e993 2023-08-14 op " padding: 5px;"
101 04e4e993 2023-08-14 op " border: 0;"
102 04e4e993 2023-08-14 op " background: transparent;"
103 04e4e993 2023-08-14 op " transition: background-color .25s ease-in-out;"
104 04e4e993 2023-08-14 op "}"
105 04e4e993 2023-08-14 op ".playlist button::before{"
106 04e4e993 2023-08-14 op " content: \"\";"
107 04e4e993 2023-08-14 op " width: 2ch;"
108 04e4e993 2023-08-14 op " display: inline-block;"
109 04e4e993 2023-08-14 op "}"
110 04e4e993 2023-08-14 op ".playlist button:hover{"
111 04e4e993 2023-08-14 op " background-color: #dfdddd;"
112 04e4e993 2023-08-14 op "}"
113 04e4e993 2023-08-14 op ".playlist #current button{"
114 04e4e993 2023-08-14 op " font-weight: bold;"
115 04e4e993 2023-08-14 op "}"
116 04e4e993 2023-08-14 op ".playlist #current button::before{"
117 04e4e993 2023-08-14 op " content: \"→ \";"
118 04e4e993 2023-08-14 op " font-weight: bold;"
119 04e4e993 2023-08-14 op "}"
120 04e4e993 2023-08-14 op ".controls{"
121 04e4e993 2023-08-14 op " position: sticky;"
122 04e4e993 2023-08-14 op " width: 100%;"
123 04e4e993 2023-08-14 op " max-width: 800px;"
124 04e4e993 2023-08-14 op " margin: 0 auto;"
125 04e4e993 2023-08-14 op " bottom: 0;"
126 04e4e993 2023-08-14 op " background-color: white;"
127 04e4e993 2023-08-14 op " background: #3d3d3d;"
128 04e4e993 2023-08-14 op " color: white;"
129 04e4e993 2023-08-14 op " border-radius: 10px 10px 0 0;"
130 04e4e993 2023-08-14 op " padding: 10px;"
131 04e4e993 2023-08-14 op " text-align: center;"
132 04e4e993 2023-08-14 op " order: 2;"
133 04e4e993 2023-08-14 op "}"
134 04e4e993 2023-08-14 op ".controls p{"
135 04e4e993 2023-08-14 op " margin: .4rem;"
136 04e4e993 2023-08-14 op "}"
137 04e4e993 2023-08-14 op ".controls a{"
138 04e4e993 2023-08-14 op " color: white;"
139 04e4e993 2023-08-14 op "}"
140 04e4e993 2023-08-14 op ".controls .status{"
141 04e4e993 2023-08-14 op " font-size: 0.9rem;"
142 04e4e993 2023-08-14 op "}"
143 04e4e993 2023-08-14 op ".controls button{"
144 04e4e993 2023-08-14 op " margin: 5px;"
145 04e4e993 2023-08-14 op " padding: 5px 20px;"
146 04e4e993 2023-08-14 op "}"
147 04e4e993 2023-08-14 op ".mode-active{"
148 04e4e993 2023-08-14 op " color: #0064ff;"
149 04e4e993 2023-08-14 op "}"
150 04e4e993 2023-08-14 op "</style>"
151 04e4e993 2023-08-14 op "</head>"
152 04e4e993 2023-08-14 op "<body>";
153 04e4e993 2023-08-14 op
154 04e4e993 2023-08-14 op const char *foot = "<script>"
155 04e4e993 2023-08-14 op "function cur(e) {"
156 04e4e993 2023-08-14 op " if (e) {e.preventDefault()}"
157 04e4e993 2023-08-14 op " let cur = document.querySelector('#current');"
158 04e4e993 2023-08-14 op " if (cur) {cur.scrollIntoView(); window.scrollBy(0, -100);}"
159 04e4e993 2023-08-14 op "}"
160 04e4e993 2023-08-14 op "cur();"
161 04e4e993 2023-08-14 op "document.querySelector('.controls a').addEventListener('click',cur)"
162 04e4e993 2023-08-14 op "</script></body></html>";
163 04e4e993 2023-08-14 op
164 04e4e993 2023-08-14 op static int
165 04e4e993 2023-08-14 op dial(const char *sock)
166 04e4e993 2023-08-14 op {
167 04e4e993 2023-08-14 op struct sockaddr_un sa;
168 04e4e993 2023-08-14 op size_t len;
169 04e4e993 2023-08-14 op int s;
170 04e4e993 2023-08-14 op
171 04e4e993 2023-08-14 op memset(&sa, 0, sizeof(sa));
172 04e4e993 2023-08-14 op sa.sun_family = AF_UNIX;
173 04e4e993 2023-08-14 op len = strlcpy(sa.sun_path, sock, sizeof(sa.sun_path));
174 04e4e993 2023-08-14 op if (len >= sizeof(sa.sun_path))
175 04e4e993 2023-08-14 op err(1, "path too long: %s", sock);
176 04e4e993 2023-08-14 op
177 04e4e993 2023-08-14 op if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
178 04e4e993 2023-08-14 op err(1, "socket");
179 04e4e993 2023-08-14 op if (connect(s, (struct sockaddr *)&sa, sizeof(sa)) == -1)
180 04e4e993 2023-08-14 op err(1, "failed to connect to %s", sock);
181 04e4e993 2023-08-14 op
182 04e4e993 2023-08-14 op return s;
183 04e4e993 2023-08-14 op }
184 04e4e993 2023-08-14 op
185 04e4e993 2023-08-14 op
186 04e4e993 2023-08-14 op /*
187 04e4e993 2023-08-14 op * Adapted from usr.sbin/httpd/httpd.c' url_decode.
188 04e4e993 2023-08-14 op */
189 04e4e993 2023-08-14 op static int
190 04e4e993 2023-08-14 op url_decode(char *url)
191 04e4e993 2023-08-14 op {
192 04e4e993 2023-08-14 op char*p, *q;
193 04e4e993 2023-08-14 op char hex[3] = {0};
194 04e4e993 2023-08-14 op unsigned long x;
195 04e4e993 2023-08-14 op
196 04e4e993 2023-08-14 op p = q = url;
197 04e4e993 2023-08-14 op while (*p != '\0') {
198 04e4e993 2023-08-14 op switch (*p) {
199 04e4e993 2023-08-14 op case '%':
200 04e4e993 2023-08-14 op /* Encoding character is followed by two hex chars */
201 04e4e993 2023-08-14 op if (!isxdigit((unsigned char)p[1]) ||
202 04e4e993 2023-08-14 op !isxdigit((unsigned char)p[2]) ||
203 04e4e993 2023-08-14 op (p[1] == '0' && p[2] == '0'))
204 04e4e993 2023-08-14 op return (-1);
205 04e4e993 2023-08-14 op
206 04e4e993 2023-08-14 op hex[0] = p[1];
207 04e4e993 2023-08-14 op hex[1] = p[2];
208 04e4e993 2023-08-14 op
209 04e4e993 2023-08-14 op /*
210 04e4e993 2023-08-14 op * We don't have to validate "hex" because it is
211 04e4e993 2023-08-14 op * guaranteed to include two hex chars followed
212 04e4e993 2023-08-14 op * by NUL.
213 04e4e993 2023-08-14 op */
214 04e4e993 2023-08-14 op x = strtoul(hex, NULL, 16);
215 04e4e993 2023-08-14 op *q = (char)x;
216 04e4e993 2023-08-14 op p += 2;
217 04e4e993 2023-08-14 op break;
218 04e4e993 2023-08-14 op case '+':
219 04e4e993 2023-08-14 op *q = ' ';
220 04e4e993 2023-08-14 op break;
221 04e4e993 2023-08-14 op default:
222 04e4e993 2023-08-14 op *q = *p;
223 04e4e993 2023-08-14 op break;
224 04e4e993 2023-08-14 op }
225 04e4e993 2023-08-14 op p++;
226 04e4e993 2023-08-14 op q++;
227 04e4e993 2023-08-14 op }
228 04e4e993 2023-08-14 op *q = '\0';
229 04e4e993 2023-08-14 op
230 04e4e993 2023-08-14 op return (0);
231 04e4e993 2023-08-14 op }
232 04e4e993 2023-08-14 op
233 04e4e993 2023-08-14 op static void
234 b478a4bd 2023-08-14 op unexpected_imsg(struct imsg *imsg, const char *expected)
235 b478a4bd 2023-08-14 op {
236 b478a4bd 2023-08-14 op const char *msg;
237 b478a4bd 2023-08-14 op size_t datalen;
238 b478a4bd 2023-08-14 op
239 b478a4bd 2023-08-14 op if (imsg->hdr.type != IMSG_CTL_ERR) {
240 b478a4bd 2023-08-14 op log_warnx("got event %d while expecting %s",
241 b478a4bd 2023-08-14 op imsg->hdr.type, expected);
242 b478a4bd 2023-08-14 op return;
243 b478a4bd 2023-08-14 op }
244 b478a4bd 2023-08-14 op
245 b478a4bd 2023-08-14 op datalen = IMSG_DATA_SIZE(*imsg);
246 b478a4bd 2023-08-14 op msg = imsg->data;
247 b478a4bd 2023-08-14 op if (datalen == 0 || msg[datalen - 1] != '\0')
248 b478a4bd 2023-08-14 op fatalx("malformed error message");
249 b478a4bd 2023-08-14 op log_warnx("failure: %s", msg);
250 b478a4bd 2023-08-14 op }
251 b478a4bd 2023-08-14 op
252 b478a4bd 2023-08-14 op static void
253 3634fa70 2023-08-31 op route_notfound(struct client *clt)
254 04e4e993 2023-08-14 op {
255 3634fa70 2023-08-31 op if (http_reply(clt, 404, "Not Found", "text/plain") == -1 ||
256 3634fa70 2023-08-31 op http_writes(clt, "Page not found\n") == -1)
257 04e4e993 2023-08-14 op return;
258 04e4e993 2023-08-14 op }
259 04e4e993 2023-08-14 op
260 04e4e993 2023-08-14 op static void
261 3634fa70 2023-08-31 op render_playlist(struct client *clt)
262 04e4e993 2023-08-14 op {
263 04e4e993 2023-08-14 op struct imsg imsg;
264 04e4e993 2023-08-14 op struct player_status ps;
265 04e4e993 2023-08-14 op ssize_t n;
266 04e4e993 2023-08-14 op const char *p;
267 04e4e993 2023-08-14 op int current, done;
268 04e4e993 2023-08-14 op
269 04e4e993 2023-08-14 op imsg_compose(&ibuf, IMSG_CTL_SHOW, 0, 0, -1, NULL, 0);
270 04e4e993 2023-08-14 op imsg_flush(&ibuf);
271 04e4e993 2023-08-14 op
272 3634fa70 2023-08-31 op http_writes(clt, "<section class='playlist-wrapper'>");
273 3634fa70 2023-08-31 op http_writes(clt, "<form action=jump method=post"
274 04e4e993 2023-08-14 op " enctype='"FORM_URLENCODED"'>");
275 3634fa70 2023-08-31 op http_writes(clt, "<ul class=playlist>");
276 04e4e993 2023-08-14 op
277 04e4e993 2023-08-14 op done = 0;
278 04e4e993 2023-08-14 op while (!done) {
279 04e4e993 2023-08-14 op if ((n = imsg_read(&ibuf)) == -1)
280 04e4e993 2023-08-14 op fatal("imsg_read");
281 04e4e993 2023-08-14 op if (n == 0)
282 04e4e993 2023-08-14 op fatalx("pipe closed");
283 04e4e993 2023-08-14 op
284 04e4e993 2023-08-14 op for (;;) {
285 04e4e993 2023-08-14 op if ((n = imsg_get(&ibuf, &imsg)) == -1)
286 04e4e993 2023-08-14 op fatal("imsg_get");
287 04e4e993 2023-08-14 op if (n == 0)
288 04e4e993 2023-08-14 op break;
289 04e4e993 2023-08-14 op
290 04e4e993 2023-08-14 op if (imsg.hdr.type != IMSG_CTL_SHOW) {
291 b478a4bd 2023-08-14 op unexpected_imsg(&imsg, "IMSG_CTL_SHOW");
292 04e4e993 2023-08-14 op imsg_free(&imsg);
293 04e4e993 2023-08-14 op continue;
294 04e4e993 2023-08-14 op }
295 04e4e993 2023-08-14 op
296 04e4e993 2023-08-14 op if (IMSG_DATA_SIZE(imsg) == 0) {
297 04e4e993 2023-08-14 op done = 1;
298 04e4e993 2023-08-14 op break;
299 04e4e993 2023-08-14 op }
300 04e4e993 2023-08-14 op
301 04e4e993 2023-08-14 op if (IMSG_DATA_SIZE(imsg) != sizeof(ps))
302 04e4e993 2023-08-14 op fatalx("wrong size for seek ctl");
303 04e4e993 2023-08-14 op memcpy(&ps, imsg.data, sizeof(ps));
304 04e4e993 2023-08-14 op if (ps.path[sizeof(ps.path) - 1] != '\0')
305 04e4e993 2023-08-14 op fatalx("received corrupted data");
306 04e4e993 2023-08-14 op
307 04e4e993 2023-08-14 op current = ps.status == STATE_PLAYING;
308 04e4e993 2023-08-14 op
309 04e4e993 2023-08-14 op p = ps.path;
310 04e4e993 2023-08-14 op if (!strncmp(p, prefix, prefixlen))
311 04e4e993 2023-08-14 op p += prefixlen;
312 04e4e993 2023-08-14 op
313 3634fa70 2023-08-31 op http_fmt(clt, "<li%s>",
314 04e4e993 2023-08-14 op current ? " id=current" : "");
315 3634fa70 2023-08-31 op http_writes(clt,
316 04e4e993 2023-08-14 op "<button type=submit name=jump value=\"");
317 3634fa70 2023-08-31 op http_htmlescape(clt, ps.path);
318 3634fa70 2023-08-31 op http_writes(clt, "\">");
319 3634fa70 2023-08-31 op http_htmlescape(clt, p);
320 3634fa70 2023-08-31 op http_writes(clt, "</button></li>");
321 04e4e993 2023-08-14 op
322 04e4e993 2023-08-14 op imsg_free(&imsg);
323 04e4e993 2023-08-14 op }
324 04e4e993 2023-08-14 op }
325 04e4e993 2023-08-14 op
326 3634fa70 2023-08-31 op http_writes(clt, "</ul>");
327 3634fa70 2023-08-31 op http_writes(clt, "</form>");
328 3634fa70 2023-08-31 op http_writes(clt, "</section>");
329 04e4e993 2023-08-14 op }
330 04e4e993 2023-08-14 op
331 04e4e993 2023-08-14 op static void
332 3634fa70 2023-08-31 op render_controls(struct client *clt)
333 04e4e993 2023-08-14 op {
334 04e4e993 2023-08-14 op struct imsg imsg;
335 04e4e993 2023-08-14 op struct player_status ps;
336 04e4e993 2023-08-14 op ssize_t n;
337 04e4e993 2023-08-14 op const char *oc, *ac, *p;
338 04e4e993 2023-08-14 op int playing;
339 04e4e993 2023-08-14 op
340 04e4e993 2023-08-14 op imsg_compose(&ibuf, IMSG_CTL_STATUS, 0, 0, -1, NULL, 0);
341 04e4e993 2023-08-14 op imsg_flush(&ibuf);
342 04e4e993 2023-08-14 op
343 04e4e993 2023-08-14 op if ((n = imsg_read(&ibuf)) == -1)
344 04e4e993 2023-08-14 op fatal("imsg_read");
345 04e4e993 2023-08-14 op if (n == 0)
346 04e4e993 2023-08-14 op fatalx("pipe closed");
347 04e4e993 2023-08-14 op
348 04e4e993 2023-08-14 op if ((n = imsg_get(&ibuf, &imsg)) == -1)
349 04e4e993 2023-08-14 op fatal("imsg_get");
350 04e4e993 2023-08-14 op if (n == 0)
351 04e4e993 2023-08-14 op return;
352 04e4e993 2023-08-14 op
353 04e4e993 2023-08-14 op if (imsg.hdr.type != IMSG_CTL_STATUS) {
354 b478a4bd 2023-08-14 op unexpected_imsg(&imsg, "IMSG_CTL_STATUS");
355 04e4e993 2023-08-14 op goto done;
356 04e4e993 2023-08-14 op }
357 04e4e993 2023-08-14 op if (IMSG_DATA_SIZE(imsg) != sizeof(ps))
358 04e4e993 2023-08-14 op fatalx("wrong size for IMSG_CTL_STATUS");
359 04e4e993 2023-08-14 op memcpy(&ps, imsg.data, sizeof(ps));
360 04e4e993 2023-08-14 op if (ps.path[sizeof(ps.path) - 1] != '\0')
361 04e4e993 2023-08-14 op fatalx("received corrupted data");
362 04e4e993 2023-08-14 op
363 04e4e993 2023-08-14 op ac = ps.mode.repeat_all ? " class='mode-active'" : "";
364 04e4e993 2023-08-14 op oc = ps.mode.repeat_one ? " class='mode-active'" : "";
365 04e4e993 2023-08-14 op playing = ps.status == STATE_PLAYING;
366 04e4e993 2023-08-14 op
367 04e4e993 2023-08-14 op if ((p = strrchr(ps.path, '/')) != NULL)
368 04e4e993 2023-08-14 op p++;
369 04e4e993 2023-08-14 op else
370 04e4e993 2023-08-14 op p = ps.path;
371 04e4e993 2023-08-14 op
372 3634fa70 2023-08-31 op if (http_writes(clt, "<section class=controls>") == -1 ||
373 3634fa70 2023-08-31 op http_writes(clt, "<p><a href='#current'>") == -1 ||
374 3634fa70 2023-08-31 op http_htmlescape(clt, p) == -1 ||
375 3634fa70 2023-08-31 op http_writes(clt, "</a></p>") == -1 ||
376 3634fa70 2023-08-31 op http_writes(clt, "<form action=ctrls method=post"
377 04e4e993 2023-08-14 op " enctype='"FORM_URLENCODED"'>") == -1 ||
378 3634fa70 2023-08-31 op http_writes(clt, "<button type=submit name=ctl value=prev>"
379 04e4e993 2023-08-14 op ICON_PREV"</button>") == -1 ||
380 3634fa70 2023-08-31 op http_fmt(clt, "<button type=submit name=ctl value=%s>"
381 04e4e993 2023-08-14 op "%s</button>", playing ? "pause" : "play",
382 04e4e993 2023-08-14 op playing ? ICON_PAUSE : ICON_PLAY) == -1 ||
383 3634fa70 2023-08-31 op http_writes(clt, "<button type=submit name=ctl value=next>"
384 04e4e993 2023-08-14 op ICON_NEXT"</button>") == -1 ||
385 3634fa70 2023-08-31 op http_writes(clt, "</form>") == -1 ||
386 3634fa70 2023-08-31 op http_writes(clt, "<form action=mode method=post"
387 3634fa70 2023-08-31 op " enctype='"FORM_URLENCODED"'>") == -1 ||
388 3634fa70 2023-08-31 op http_fmt(clt, "<button%s type=submit name=mode value=all>"
389 04e4e993 2023-08-14 op ICON_REPEAT_ALL"</button>", ac) == -1 ||
390 3634fa70 2023-08-31 op http_fmt(clt, "<button%s type=submit name=mode value=one>"
391 04e4e993 2023-08-14 op ICON_REPEAT_ONE"</button>", oc) == -1 ||
392 3634fa70 2023-08-31 op http_writes(clt, "</form>") == -1 ||
393 3634fa70 2023-08-31 op http_writes(clt, "</section>") == -1)
394 04e4e993 2023-08-14 op return;
395 04e4e993 2023-08-14 op
396 04e4e993 2023-08-14 op done:
397 04e4e993 2023-08-14 op imsg_free(&imsg);
398 04e4e993 2023-08-14 op }
399 04e4e993 2023-08-14 op
400 04e4e993 2023-08-14 op static void
401 3634fa70 2023-08-31 op route_home(struct client *clt)
402 04e4e993 2023-08-14 op {
403 3634fa70 2023-08-31 op if (http_reply(clt, 200, "OK", "text/html;charset=UTF-8") == -1)
404 04e4e993 2023-08-14 op return;
405 04e4e993 2023-08-14 op
406 3634fa70 2023-08-31 op if (http_write(clt, head, strlen(head)) == -1)
407 04e4e993 2023-08-14 op return;
408 04e4e993 2023-08-14 op
409 3634fa70 2023-08-31 op if (http_writes(clt, "<main>") == -1)
410 04e4e993 2023-08-14 op return;
411 04e4e993 2023-08-14 op
412 3634fa70 2023-08-31 op if (http_writes(clt, "<section class=searchbox>"
413 04e4e993 2023-08-14 op "<input type=search name=filter aria-label='Filter playlist'"
414 04e4e993 2023-08-14 op " placeholder='Filter playlist' id=search />"
415 04e4e993 2023-08-14 op "</section>") == -1)
416 04e4e993 2023-08-14 op return;
417 04e4e993 2023-08-14 op
418 3634fa70 2023-08-31 op render_controls(clt);
419 3634fa70 2023-08-31 op render_playlist(clt);
420 04e4e993 2023-08-14 op
421 3634fa70 2023-08-31 op if (http_writes(clt, "</main>") == -1)
422 04e4e993 2023-08-14 op return;
423 04e4e993 2023-08-14 op
424 3634fa70 2023-08-31 op http_write(clt, foot, strlen(foot));
425 04e4e993 2023-08-14 op }
426 04e4e993 2023-08-14 op
427 04e4e993 2023-08-14 op static void
428 3634fa70 2023-08-31 op route_jump(struct client *clt)
429 04e4e993 2023-08-14 op {
430 04e4e993 2023-08-14 op struct imsg imsg;
431 04e4e993 2023-08-14 op struct player_status ps;
432 04e4e993 2023-08-14 op ssize_t n;
433 04e4e993 2023-08-14 op char path[PATH_MAX];
434 04e4e993 2023-08-14 op char *form, *field;
435 04e4e993 2023-08-14 op int found = 0;
436 04e4e993 2023-08-14 op
437 3634fa70 2023-08-31 op form = clt->buf;
438 04e4e993 2023-08-14 op while ((field = strsep(&form, "&")) != NULL) {
439 04e4e993 2023-08-14 op if (url_decode(field) == -1)
440 04e4e993 2023-08-14 op goto badreq;
441 04e4e993 2023-08-14 op
442 04e4e993 2023-08-14 op if (strncmp(field, "jump=", 5) != 0)
443 04e4e993 2023-08-14 op continue;
444 04e4e993 2023-08-14 op field += 5;
445 04e4e993 2023-08-14 op found = 1;
446 04e4e993 2023-08-14 op
447 99b28969 2023-08-14 op memset(&path, 0, sizeof(path));
448 04e4e993 2023-08-14 op if (strlcpy(path, field, sizeof(path)) >= sizeof(path))
449 04e4e993 2023-08-14 op goto badreq;
450 04e4e993 2023-08-14 op
451 04e4e993 2023-08-14 op log_warnx("path is %s", path);
452 04e4e993 2023-08-14 op imsg_compose(&ibuf, IMSG_CTL_JUMP, 0, 0, -1,
453 04e4e993 2023-08-14 op path, sizeof(path));
454 04e4e993 2023-08-14 op imsg_flush(&ibuf);
455 04e4e993 2023-08-14 op
456 04e4e993 2023-08-14 op if ((n = imsg_read(&ibuf)) == -1)
457 04e4e993 2023-08-14 op fatal("imsg_read");
458 04e4e993 2023-08-14 op if (n == 0)
459 04e4e993 2023-08-14 op fatalx("pipe closed");
460 04e4e993 2023-08-14 op
461 04e4e993 2023-08-14 op for (;;) {
462 04e4e993 2023-08-14 op if ((n = imsg_get(&ibuf, &imsg)) == -1)
463 04e4e993 2023-08-14 op fatal("imsg_get");
464 04e4e993 2023-08-14 op if (n == 0)
465 04e4e993 2023-08-14 op break;
466 04e4e993 2023-08-14 op
467 04e4e993 2023-08-14 op if (imsg.hdr.type != IMSG_CTL_STATUS) {
468 b478a4bd 2023-08-14 op unexpected_imsg(&imsg, "IMSG_CTL_STATUS");
469 04e4e993 2023-08-14 op imsg_free(&imsg);
470 04e4e993 2023-08-14 op continue;
471 04e4e993 2023-08-14 op }
472 04e4e993 2023-08-14 op
473 04e4e993 2023-08-14 op if (IMSG_DATA_SIZE(imsg) != sizeof(ps))
474 04e4e993 2023-08-14 op fatalx("data size mismatch");
475 04e4e993 2023-08-14 op memcpy(&ps, imsg.data, sizeof(ps));
476 04e4e993 2023-08-14 op if (ps.path[sizeof(ps.path) - 1] != '\0')
477 04e4e993 2023-08-14 op fatalx("received corrupted data");
478 04e4e993 2023-08-14 op log_debug("jumped to %s", ps.path);
479 04e4e993 2023-08-14 op }
480 04e4e993 2023-08-14 op
481 04e4e993 2023-08-14 op break;
482 04e4e993 2023-08-14 op }
483 04e4e993 2023-08-14 op
484 04e4e993 2023-08-14 op if (!found)
485 04e4e993 2023-08-14 op goto badreq;
486 04e4e993 2023-08-14 op
487 3634fa70 2023-08-31 op http_reply(clt, 302, "See Other", "/");
488 04e4e993 2023-08-14 op return;
489 04e4e993 2023-08-14 op
490 04e4e993 2023-08-14 op badreq:
491 3634fa70 2023-08-31 op http_reply(clt, 400, "Bad Request", "text/plain");
492 3634fa70 2023-08-31 op http_writes(clt, "Bad Request.\n");
493 04e4e993 2023-08-14 op }
494 04e4e993 2023-08-14 op
495 04e4e993 2023-08-14 op static void
496 3634fa70 2023-08-31 op route_controls(struct client *clt)
497 04e4e993 2023-08-14 op {
498 04e4e993 2023-08-14 op char *form, *field;
499 04e4e993 2023-08-14 op int cmd, found = 0;
500 04e4e993 2023-08-14 op
501 3634fa70 2023-08-31 op form = clt->buf;
502 04e4e993 2023-08-14 op while ((field = strsep(&form, "&")) != NULL) {
503 04e4e993 2023-08-14 op if (url_decode(field) == -1)
504 04e4e993 2023-08-14 op goto badreq;
505 04e4e993 2023-08-14 op
506 04e4e993 2023-08-14 op if (strncmp(field, "ctl=", 4) != 0)
507 04e4e993 2023-08-14 op continue;
508 04e4e993 2023-08-14 op field += 4;
509 04e4e993 2023-08-14 op found = 1;
510 04e4e993 2023-08-14 op
511 04e4e993 2023-08-14 op if (!strcmp(field, "play"))
512 04e4e993 2023-08-14 op cmd = IMSG_CTL_PLAY;
513 04e4e993 2023-08-14 op else if (!strcmp(field, "pause"))
514 04e4e993 2023-08-14 op cmd = IMSG_CTL_PAUSE;
515 04e4e993 2023-08-14 op else if (!strcmp(field, "next"))
516 04e4e993 2023-08-14 op cmd = IMSG_CTL_NEXT;
517 04e4e993 2023-08-14 op else if (!strcmp(field, "prev"))
518 04e4e993 2023-08-14 op cmd = IMSG_CTL_PREV;
519 04e4e993 2023-08-14 op else
520 04e4e993 2023-08-14 op goto badreq;
521 04e4e993 2023-08-14 op
522 04e4e993 2023-08-14 op imsg_compose(&ibuf, cmd, 0, 0, -1, NULL, 0);
523 04e4e993 2023-08-14 op imsg_flush(&ibuf);
524 04e4e993 2023-08-14 op break;
525 04e4e993 2023-08-14 op }
526 04e4e993 2023-08-14 op
527 04e4e993 2023-08-14 op if (!found)
528 04e4e993 2023-08-14 op goto badreq;
529 04e4e993 2023-08-14 op
530 3634fa70 2023-08-31 op http_reply(clt, 302, "See Other", "/");
531 04e4e993 2023-08-14 op return;
532 04e4e993 2023-08-14 op
533 04e4e993 2023-08-14 op badreq:
534 3634fa70 2023-08-31 op http_reply(clt, 400, "Bad Request", "text/plain");
535 3634fa70 2023-08-31 op http_writes(clt, "Bad Request.\n");
536 04e4e993 2023-08-14 op }
537 04e4e993 2023-08-14 op
538 04e4e993 2023-08-14 op static void
539 3634fa70 2023-08-31 op route_mode(struct client *clt)
540 04e4e993 2023-08-14 op {
541 04e4e993 2023-08-14 op char *form, *field;
542 04e4e993 2023-08-14 op int found = 0;
543 04e4e993 2023-08-14 op ssize_t n;
544 04e4e993 2023-08-14 op struct player_status ps;
545 04e4e993 2023-08-14 op struct player_mode pm;
546 04e4e993 2023-08-14 op struct imsg imsg;
547 04e4e993 2023-08-14 op
548 04e4e993 2023-08-14 op pm.repeat_one = pm.repeat_all = pm.consume = MODE_UNDEF;
549 04e4e993 2023-08-14 op
550 3634fa70 2023-08-31 op form = clt->buf;
551 04e4e993 2023-08-14 op while ((field = strsep(&form, "&")) != NULL) {
552 04e4e993 2023-08-14 op if (url_decode(field) == -1)
553 04e4e993 2023-08-14 op goto badreq;
554 04e4e993 2023-08-14 op
555 04e4e993 2023-08-14 op if (strncmp(field, "mode=", 5) != 0)
556 04e4e993 2023-08-14 op continue;
557 04e4e993 2023-08-14 op field += 5;
558 04e4e993 2023-08-14 op found = 1;
559 04e4e993 2023-08-14 op
560 04e4e993 2023-08-14 op if (!strcmp(field, "all"))
561 04e4e993 2023-08-14 op pm.repeat_all = MODE_TOGGLE;
562 04e4e993 2023-08-14 op else if (!strcmp(field, "one"))
563 04e4e993 2023-08-14 op pm.repeat_one = MODE_TOGGLE;
564 04e4e993 2023-08-14 op else
565 04e4e993 2023-08-14 op goto badreq;
566 04e4e993 2023-08-14 op
567 04e4e993 2023-08-14 op imsg_compose(&ibuf, IMSG_CTL_MODE, 0, 0, -1, &pm, sizeof(pm));
568 04e4e993 2023-08-14 op imsg_flush(&ibuf);
569 04e4e993 2023-08-14 op
570 04e4e993 2023-08-14 op if ((n = imsg_read(&ibuf)) == -1)
571 04e4e993 2023-08-14 op fatal("imsg_read");
572 04e4e993 2023-08-14 op if (n == 0)
573 04e4e993 2023-08-14 op fatalx("pipe closed");
574 04e4e993 2023-08-14 op
575 04e4e993 2023-08-14 op for (;;) {
576 04e4e993 2023-08-14 op if ((n = imsg_get(&ibuf, &imsg)) == -1)
577 04e4e993 2023-08-14 op fatal("imsg_get");
578 04e4e993 2023-08-14 op if (n == 0)
579 04e4e993 2023-08-14 op break;
580 04e4e993 2023-08-14 op
581 04e4e993 2023-08-14 op if (imsg.hdr.type != IMSG_CTL_STATUS) {
582 b478a4bd 2023-08-14 op unexpected_imsg(&imsg, "IMSG_CTL_STATUS");
583 04e4e993 2023-08-14 op imsg_free(&imsg);
584 04e4e993 2023-08-14 op continue;
585 04e4e993 2023-08-14 op }
586 04e4e993 2023-08-14 op
587 04e4e993 2023-08-14 op if (IMSG_DATA_SIZE(imsg) != sizeof(ps))
588 04e4e993 2023-08-14 op fatalx("data size mismatch");
589 04e4e993 2023-08-14 op memcpy(&ps, imsg.data, sizeof(ps));
590 04e4e993 2023-08-14 op if (ps.path[sizeof(ps.path) - 1] != '\0')
591 04e4e993 2023-08-14 op fatalx("received corrupted data");
592 04e4e993 2023-08-14 op }
593 04e4e993 2023-08-14 op
594 04e4e993 2023-08-14 op break;
595 04e4e993 2023-08-14 op }
596 04e4e993 2023-08-14 op
597 04e4e993 2023-08-14 op if (!found)
598 04e4e993 2023-08-14 op goto badreq;
599 04e4e993 2023-08-14 op
600 3634fa70 2023-08-31 op http_reply(clt, 302, "See Other", "/");
601 04e4e993 2023-08-14 op return;
602 04e4e993 2023-08-14 op
603 04e4e993 2023-08-14 op badreq:
604 3634fa70 2023-08-31 op http_reply(clt, 400, "Bad Request", "text/plain");
605 3634fa70 2023-08-31 op http_writes(clt, "Bad Request.\n");
606 04e4e993 2023-08-14 op }
607 04e4e993 2023-08-14 op
608 04e4e993 2023-08-14 op static void
609 3634fa70 2023-08-31 op route_dispatch(struct client *clt)
610 04e4e993 2023-08-14 op {
611 04e4e993 2023-08-14 op static const struct route {
612 3634fa70 2023-08-31 op int method;
613 3634fa70 2023-08-31 op const char *path;
614 3634fa70 2023-08-31 op route_fn route;
615 04e4e993 2023-08-14 op } routes[] = {
616 04e4e993 2023-08-14 op { METHOD_GET, "/", &route_home },
617 04e4e993 2023-08-14 op { METHOD_POST, "/jump", &route_jump },
618 04e4e993 2023-08-14 op { METHOD_POST, "/ctrls", &route_controls },
619 04e4e993 2023-08-14 op { METHOD_POST, "/mode", &route_mode },
620 04e4e993 2023-08-14 op
621 04e4e993 2023-08-14 op { METHOD_GET, "*", &route_notfound },
622 04e4e993 2023-08-14 op { METHOD_POST, "*", &route_notfound },
623 04e4e993 2023-08-14 op };
624 3634fa70 2023-08-31 op struct request *req = &clt->req;
625 04e4e993 2023-08-14 op size_t i;
626 04e4e993 2023-08-14 op
627 04e4e993 2023-08-14 op if ((req->method != METHOD_GET && req->method != METHOD_POST) ||
628 04e4e993 2023-08-14 op (req->ctype != NULL && strcmp(req->ctype, FORM_URLENCODED) != 0) ||
629 04e4e993 2023-08-14 op req->path == NULL) {
630 3634fa70 2023-08-31 op http_reply(clt, 400, "Bad Request", NULL);
631 04e4e993 2023-08-14 op return;
632 04e4e993 2023-08-14 op }
633 04e4e993 2023-08-14 op
634 04e4e993 2023-08-14 op for (i = 0; i < nitems(routes); ++i) {
635 04e4e993 2023-08-14 op if (req->method != routes[i].method ||
636 04e4e993 2023-08-14 op fnmatch(routes[i].path, req->path, 0) != 0)
637 04e4e993 2023-08-14 op continue;
638 3634fa70 2023-08-31 op clt->done = 1; /* assume with one round is done */
639 3634fa70 2023-08-31 op clt->route = routes[i].route;
640 3634fa70 2023-08-31 op clt->route(clt);
641 3634fa70 2023-08-31 op if (clt->done)
642 3634fa70 2023-08-31 op http_close(clt);
643 04e4e993 2023-08-14 op return;
644 04e4e993 2023-08-14 op }
645 04e4e993 2023-08-14 op }
646 04e4e993 2023-08-14 op
647 04e4e993 2023-08-14 op static void
648 3634fa70 2023-08-31 op client_ev(int fd, int ev, void *d)
649 3634fa70 2023-08-31 op {
650 3634fa70 2023-08-31 op struct client *clt = d;
651 3634fa70 2023-08-31 op
652 3634fa70 2023-08-31 op if (ev & (POLLIN|POLLHUP)) {
653 3634fa70 2023-08-31 op if (bufio_read(&clt->bio) == -1 && errno != EAGAIN) {
654 3634fa70 2023-08-31 op log_warn("bufio_read");
655 3634fa70 2023-08-31 op goto err;
656 3634fa70 2023-08-31 op }
657 3634fa70 2023-08-31 op }
658 3634fa70 2023-08-31 op
659 3634fa70 2023-08-31 op if (ev & POLLOUT) {
660 3634fa70 2023-08-31 op if (bufio_write(&clt->bio) == -1 && errno != EAGAIN) {
661 3634fa70 2023-08-31 op log_warn("bufio_read");
662 3634fa70 2023-08-31 op goto err;
663 3634fa70 2023-08-31 op }
664 3634fa70 2023-08-31 op }
665 3634fa70 2023-08-31 op
666 3634fa70 2023-08-31 op if (clt->route == NULL) {
667 3634fa70 2023-08-31 op if (http_parse(clt) == -1) {
668 3634fa70 2023-08-31 op if (errno == EAGAIN)
669 3634fa70 2023-08-31 op goto again;
670 3634fa70 2023-08-31 op log_warnx("HTTP parse request failed");
671 3634fa70 2023-08-31 op goto err;
672 3634fa70 2023-08-31 op }
673 3634fa70 2023-08-31 op if (clt->req.method == METHOD_POST &&
674 3634fa70 2023-08-31 op http_read(clt) == -1) {
675 3634fa70 2023-08-31 op if (errno == EAGAIN)
676 3634fa70 2023-08-31 op goto again;
677 3634fa70 2023-08-31 op log_warnx("failed to read POST data");
678 3634fa70 2023-08-31 op goto err;
679 3634fa70 2023-08-31 op }
680 3634fa70 2023-08-31 op route_dispatch(clt);
681 3634fa70 2023-08-31 op goto again;
682 3634fa70 2023-08-31 op }
683 3634fa70 2023-08-31 op
684 3634fa70 2023-08-31 op if (!clt->done)
685 3634fa70 2023-08-31 op clt->route(clt);
686 3634fa70 2023-08-31 op
687 3634fa70 2023-08-31 op again:
688 3634fa70 2023-08-31 op ev = bufio_pollev(&clt->bio);
689 3634fa70 2023-08-31 op if (ev == POLLIN && clt->done) {
690 3634fa70 2023-08-31 op goto err; /* done with this client */
691 3634fa70 2023-08-31 op }
692 3634fa70 2023-08-31 op
693 3634fa70 2023-08-31 op ev_add(fd, ev, client_ev, clt);
694 3634fa70 2023-08-31 op return;
695 3634fa70 2023-08-31 op
696 3634fa70 2023-08-31 op err:
697 3634fa70 2023-08-31 op ev_del(fd);
698 3634fa70 2023-08-31 op http_free(clt);
699 3634fa70 2023-08-31 op }
700 3634fa70 2023-08-31 op
701 3634fa70 2023-08-31 op static void
702 703c260b 2023-08-25 op web_accept(int psock, int ev, void *d)
703 04e4e993 2023-08-14 op {
704 3634fa70 2023-08-31 op struct client *clt;
705 077802c1 2023-08-25 op int sock;
706 04e4e993 2023-08-14 op
707 04e4e993 2023-08-14 op if ((sock = accept(psock, NULL, NULL)) == -1) {
708 04e4e993 2023-08-14 op warn("accept");
709 04e4e993 2023-08-14 op return;
710 04e4e993 2023-08-14 op }
711 3634fa70 2023-08-31 op clt = xcalloc(1, sizeof(*clt));
712 3634fa70 2023-08-31 op if ((clt = calloc(1, sizeof(*clt))) == NULL ||
713 3634fa70 2023-08-31 op http_init(clt, sock) == -1) {
714 3634fa70 2023-08-31 op log_warn("failed to initialize client");
715 3634fa70 2023-08-31 op free(clt);
716 04e4e993 2023-08-14 op close(sock);
717 04e4e993 2023-08-14 op return;
718 04e4e993 2023-08-14 op }
719 3634fa70 2023-08-31 op
720 3634fa70 2023-08-31 op client_ev(sock, POLLIN, clt);
721 04e4e993 2023-08-14 op return;
722 04e4e993 2023-08-14 op }
723 04e4e993 2023-08-14 op
724 04e4e993 2023-08-14 op void __dead
725 04e4e993 2023-08-14 op usage(void)
726 04e4e993 2023-08-14 op {
727 04e4e993 2023-08-14 op fprintf(stderr, "usage: %s [-v] [-s sock] [-t prefix] [[host] port]\n",
728 04e4e993 2023-08-14 op getprogname());
729 04e4e993 2023-08-14 op exit(1);
730 04e4e993 2023-08-14 op }
731 04e4e993 2023-08-14 op
732 04e4e993 2023-08-14 op int
733 04e4e993 2023-08-14 op main(int argc, char **argv)
734 04e4e993 2023-08-14 op {
735 04e4e993 2023-08-14 op struct addrinfo hints, *res, *res0;
736 04e4e993 2023-08-14 op const char *cause = NULL;
737 04e4e993 2023-08-14 op const char *host = NULL;
738 04e4e993 2023-08-14 op const char *port = "9090";
739 04e4e993 2023-08-14 op char *sock = NULL;
740 80d5f5ad 2023-08-25 op size_t nsock, error, save_errno;
741 80d5f5ad 2023-08-25 op int ch, v, amused_sock, fd;
742 04e4e993 2023-08-14 op int verbose = 0;
743 04e4e993 2023-08-14 op
744 04e4e993 2023-08-14 op setlocale(LC_ALL, NULL);
745 04e4e993 2023-08-14 op
746 04e4e993 2023-08-14 op log_init(1, LOG_DAEMON);
747 04e4e993 2023-08-14 op
748 04e4e993 2023-08-14 op if (pledge("stdio rpath unix inet dns", NULL) == -1)
749 04e4e993 2023-08-14 op err(1, "pledge");
750 04e4e993 2023-08-14 op
751 04e4e993 2023-08-14 op while ((ch = getopt(argc, argv, "s:t:v")) != -1) {
752 04e4e993 2023-08-14 op switch (ch) {
753 04e4e993 2023-08-14 op case 's':
754 04e4e993 2023-08-14 op sock = optarg;
755 04e4e993 2023-08-14 op break;
756 04e4e993 2023-08-14 op case 't':
757 04e4e993 2023-08-14 op prefix = optarg;
758 04e4e993 2023-08-14 op prefixlen = strlen(prefix);
759 04e4e993 2023-08-14 op break;
760 04e4e993 2023-08-14 op case 'v':
761 04e4e993 2023-08-14 op verbose = 1;
762 04e4e993 2023-08-14 op break;
763 04e4e993 2023-08-14 op default:
764 04e4e993 2023-08-14 op usage();
765 04e4e993 2023-08-14 op }
766 04e4e993 2023-08-14 op }
767 04e4e993 2023-08-14 op argc -= optind;
768 04e4e993 2023-08-14 op argv += optind;
769 04e4e993 2023-08-14 op
770 04e4e993 2023-08-14 op if (argc == 1)
771 04e4e993 2023-08-14 op port = argv[0];
772 04e4e993 2023-08-14 op if (argc == 2) {
773 04e4e993 2023-08-14 op host = argv[0];
774 04e4e993 2023-08-14 op port = argv[1];
775 04e4e993 2023-08-14 op }
776 04e4e993 2023-08-14 op if (argc > 2)
777 04e4e993 2023-08-14 op usage();
778 04e4e993 2023-08-14 op
779 04e4e993 2023-08-14 op log_setverbose(verbose);
780 04e4e993 2023-08-14 op
781 04e4e993 2023-08-14 op if (sock == NULL)
782 04e4e993 2023-08-14 op xasprintf(&sock, "/tmp/amused-%d", getuid());
783 04e4e993 2023-08-14 op
784 04e4e993 2023-08-14 op signal(SIGPIPE, SIG_IGN);
785 04e4e993 2023-08-14 op
786 80d5f5ad 2023-08-25 op if (ev_init() == -1)
787 80d5f5ad 2023-08-25 op fatal("ev_init");
788 80d5f5ad 2023-08-25 op
789 04e4e993 2023-08-14 op amused_sock = dial(sock);
790 04e4e993 2023-08-14 op imsg_init(&ibuf, amused_sock);
791 04e4e993 2023-08-14 op
792 04e4e993 2023-08-14 op memset(&hints, 0, sizeof(hints));
793 04e4e993 2023-08-14 op hints.ai_family = AF_UNSPEC;
794 04e4e993 2023-08-14 op hints.ai_socktype = SOCK_STREAM;
795 04e4e993 2023-08-14 op hints.ai_flags = AI_PASSIVE;
796 04e4e993 2023-08-14 op error = getaddrinfo(host, port, &hints, &res0);
797 04e4e993 2023-08-14 op if (error)
798 04e4e993 2023-08-14 op errx(1, "%s", gai_strerror(error));
799 04e4e993 2023-08-14 op
800 04e4e993 2023-08-14 op nsock = 0;
801 80d5f5ad 2023-08-25 op for (res = res0; res; res = res->ai_next) {
802 80d5f5ad 2023-08-25 op fd = socket(res->ai_family, res->ai_socktype,
803 04e4e993 2023-08-14 op res->ai_protocol);
804 80d5f5ad 2023-08-25 op if (fd == -1) {
805 04e4e993 2023-08-14 op cause = "socket";
806 04e4e993 2023-08-14 op continue;
807 04e4e993 2023-08-14 op }
808 04e4e993 2023-08-14 op
809 04e4e993 2023-08-14 op v = 1;
810 80d5f5ad 2023-08-25 op if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
811 04e4e993 2023-08-14 op &v, sizeof(v)) == -1)
812 04e4e993 2023-08-14 op fatal("setsockopt(SO_REUSEADDR)");
813 04e4e993 2023-08-14 op
814 80d5f5ad 2023-08-25 op if (bind(fd, res->ai_addr, res->ai_addrlen) == -1) {
815 04e4e993 2023-08-14 op cause = "bind";
816 04e4e993 2023-08-14 op save_errno = errno;
817 80d5f5ad 2023-08-25 op close(fd);
818 04e4e993 2023-08-14 op errno = save_errno;
819 04e4e993 2023-08-14 op continue;
820 04e4e993 2023-08-14 op }
821 04e4e993 2023-08-14 op
822 80d5f5ad 2023-08-25 op if (listen(fd, 5) == -1)
823 04e4e993 2023-08-14 op err(1, "listen");
824 04e4e993 2023-08-14 op
825 703c260b 2023-08-25 op if (ev_add(fd, POLLIN, web_accept, NULL) == -1)
826 80d5f5ad 2023-08-25 op fatal("ev_add");
827 04e4e993 2023-08-14 op nsock++;
828 04e4e993 2023-08-14 op }
829 04e4e993 2023-08-14 op if (nsock == 0)
830 04e4e993 2023-08-14 op err(1, "%s", cause);
831 04e4e993 2023-08-14 op freeaddrinfo(res0);
832 04e4e993 2023-08-14 op
833 04e4e993 2023-08-14 op if (pledge("stdio inet", NULL) == -1)
834 04e4e993 2023-08-14 op err(1, "pledge");
835 04e4e993 2023-08-14 op
836 04e4e993 2023-08-14 op log_info("starting");
837 80d5f5ad 2023-08-25 op ev_loop();
838 80d5f5ad 2023-08-25 op return (1);
839 04e4e993 2023-08-14 op }