Blame


1 94a3f4e9 2024-03-30 thomas /*
2 94a3f4e9 2024-03-30 thomas * Copyright (c) 2024 Omar Polo <op@openbsd.org>
3 94a3f4e9 2024-03-30 thomas *
4 94a3f4e9 2024-03-30 thomas * Permission to use, copy, modify, and distribute this software for any
5 94a3f4e9 2024-03-30 thomas * purpose with or without fee is hereby granted, provided that the above
6 94a3f4e9 2024-03-30 thomas * copyright notice and this permission notice appear in all copies.
7 94a3f4e9 2024-03-30 thomas *
8 94a3f4e9 2024-03-30 thomas * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 94a3f4e9 2024-03-30 thomas * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 94a3f4e9 2024-03-30 thomas * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 94a3f4e9 2024-03-30 thomas * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 94a3f4e9 2024-03-30 thomas * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 94a3f4e9 2024-03-30 thomas * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 94a3f4e9 2024-03-30 thomas * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 94a3f4e9 2024-03-30 thomas */
16 94a3f4e9 2024-03-30 thomas
17 94a3f4e9 2024-03-30 thomas #include <sys/time.h>
18 94a3f4e9 2024-03-30 thomas #include <sys/types.h>
19 94a3f4e9 2024-03-30 thomas #include <sys/socket.h>
20 94a3f4e9 2024-03-30 thomas
21 94a3f4e9 2024-03-30 thomas #include <err.h>
22 94a3f4e9 2024-03-30 thomas #include <errno.h>
23 94a3f4e9 2024-03-30 thomas #include <fcntl.h>
24 94a3f4e9 2024-03-30 thomas #include <limits.h>
25 94a3f4e9 2024-03-30 thomas #include <netdb.h>
26 94a3f4e9 2024-03-30 thomas #include <poll.h>
27 94a3f4e9 2024-03-30 thomas #include <stdio.h>
28 94a3f4e9 2024-03-30 thomas #include <stdlib.h>
29 94a3f4e9 2024-03-30 thomas #include <string.h>
30 94a3f4e9 2024-03-30 thomas #include <unistd.h>
31 94a3f4e9 2024-03-30 thomas
32 94a3f4e9 2024-03-30 thomas #include "got_opentemp.h"
33 94a3f4e9 2024-03-30 thomas #include "got_version.h"
34 94a3f4e9 2024-03-30 thomas
35 94a3f4e9 2024-03-30 thomas #include "bufio.h"
36 fcfdd0a1 2024-03-30 thomas #include "utf8d.h"
37 94a3f4e9 2024-03-30 thomas
38 94a3f4e9 2024-03-30 thomas #define USERAGENT "got-notify-http/" GOT_VERSION_STR
39 94a3f4e9 2024-03-30 thomas
40 94a3f4e9 2024-03-30 thomas static int http_timeout = 300; /* 5 minutes in seconds */
41 94a3f4e9 2024-03-30 thomas
42 94a3f4e9 2024-03-30 thomas __dead static void
43 94a3f4e9 2024-03-30 thomas usage(void)
44 94a3f4e9 2024-03-30 thomas {
45 94a3f4e9 2024-03-30 thomas fprintf(stderr, "usage: %s [-c] -h host -p port path\n",
46 94a3f4e9 2024-03-30 thomas getprogname());
47 94a3f4e9 2024-03-30 thomas exit(1);
48 94a3f4e9 2024-03-30 thomas }
49 94a3f4e9 2024-03-30 thomas
50 94a3f4e9 2024-03-30 thomas static int
51 94a3f4e9 2024-03-30 thomas dial(const char *host, const char *port)
52 94a3f4e9 2024-03-30 thomas {
53 94a3f4e9 2024-03-30 thomas struct addrinfo hints, *res, *res0;
54 94a3f4e9 2024-03-30 thomas const char *cause = NULL;
55 94a3f4e9 2024-03-30 thomas int s, error, save_errno;
56 94a3f4e9 2024-03-30 thomas
57 94a3f4e9 2024-03-30 thomas memset(&hints, 0, sizeof(hints));
58 94a3f4e9 2024-03-30 thomas hints.ai_family = AF_UNSPEC;
59 94a3f4e9 2024-03-30 thomas hints.ai_socktype = SOCK_STREAM;
60 94a3f4e9 2024-03-30 thomas error = getaddrinfo(host, port, &hints, &res0);
61 94a3f4e9 2024-03-30 thomas if (error)
62 94a3f4e9 2024-03-30 thomas errx(1, "failed to resolve %s:%s: %s", host, port,
63 94a3f4e9 2024-03-30 thomas gai_strerror(error));
64 94a3f4e9 2024-03-30 thomas
65 94a3f4e9 2024-03-30 thomas s = -1;
66 94a3f4e9 2024-03-30 thomas for (res = res0; res; res = res->ai_next) {
67 94a3f4e9 2024-03-30 thomas s = socket(res->ai_family, res->ai_socktype,
68 94a3f4e9 2024-03-30 thomas res->ai_protocol);
69 94a3f4e9 2024-03-30 thomas if (s == -1) {
70 94a3f4e9 2024-03-30 thomas cause = "socket";
71 94a3f4e9 2024-03-30 thomas continue;
72 94a3f4e9 2024-03-30 thomas }
73 94a3f4e9 2024-03-30 thomas
74 94a3f4e9 2024-03-30 thomas if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
75 94a3f4e9 2024-03-30 thomas cause = "connect";
76 94a3f4e9 2024-03-30 thomas save_errno = errno;
77 94a3f4e9 2024-03-30 thomas close(s);
78 94a3f4e9 2024-03-30 thomas errno = save_errno;
79 94a3f4e9 2024-03-30 thomas s = -1;
80 94a3f4e9 2024-03-30 thomas continue;
81 94a3f4e9 2024-03-30 thomas }
82 94a3f4e9 2024-03-30 thomas
83 94a3f4e9 2024-03-30 thomas break;
84 94a3f4e9 2024-03-30 thomas }
85 94a3f4e9 2024-03-30 thomas
86 94a3f4e9 2024-03-30 thomas freeaddrinfo(res0);
87 94a3f4e9 2024-03-30 thomas if (s == -1)
88 94a3f4e9 2024-03-30 thomas err(1, "%s", cause);
89 94a3f4e9 2024-03-30 thomas return s;
90 94a3f4e9 2024-03-30 thomas }
91 94a3f4e9 2024-03-30 thomas
92 94a3f4e9 2024-03-30 thomas static void
93 94a3f4e9 2024-03-30 thomas escape(FILE *fp, const uint8_t *s)
94 94a3f4e9 2024-03-30 thomas {
95 fcfdd0a1 2024-03-30 thomas uint32_t codepoint, state;
96 fcfdd0a1 2024-03-30 thomas const uint8_t *start = s;
97 94a3f4e9 2024-03-30 thomas
98 fcfdd0a1 2024-03-30 thomas state = 0;
99 fcfdd0a1 2024-03-30 thomas for (; *s; ++s) {
100 fcfdd0a1 2024-03-30 thomas switch (decode(&state, &codepoint, *s)) {
101 fcfdd0a1 2024-03-30 thomas case UTF8_ACCEPT:
102 fcfdd0a1 2024-03-30 thomas switch (codepoint) {
103 fcfdd0a1 2024-03-30 thomas case '"':
104 fcfdd0a1 2024-03-30 thomas case '\\':
105 fcfdd0a1 2024-03-30 thomas fprintf(fp, "\\%c", *s);
106 fcfdd0a1 2024-03-30 thomas break;
107 fcfdd0a1 2024-03-30 thomas case '\b':
108 fcfdd0a1 2024-03-30 thomas fprintf(fp, "\\b");
109 fcfdd0a1 2024-03-30 thomas break;
110 fcfdd0a1 2024-03-30 thomas case '\f':
111 fcfdd0a1 2024-03-30 thomas fprintf(fp, "\\f");
112 fcfdd0a1 2024-03-30 thomas break;
113 fcfdd0a1 2024-03-30 thomas case '\n':
114 fcfdd0a1 2024-03-30 thomas fprintf(fp, "\\n");
115 fcfdd0a1 2024-03-30 thomas break;
116 fcfdd0a1 2024-03-30 thomas case '\r':
117 fcfdd0a1 2024-03-30 thomas fprintf(fp, "\\r");
118 fcfdd0a1 2024-03-30 thomas break;
119 fcfdd0a1 2024-03-30 thomas case '\t':
120 fcfdd0a1 2024-03-30 thomas fprintf(fp, "\\t");
121 fcfdd0a1 2024-03-30 thomas break;
122 fcfdd0a1 2024-03-30 thomas default:
123 fcfdd0a1 2024-03-30 thomas /* other control characters */
124 fcfdd0a1 2024-03-30 thomas if (codepoint < ' ' || codepoint == 0x7F) {
125 fcfdd0a1 2024-03-30 thomas fprintf(fp, "\\u%04x", codepoint);
126 fcfdd0a1 2024-03-30 thomas break;
127 fcfdd0a1 2024-03-30 thomas }
128 fcfdd0a1 2024-03-30 thomas fwrite(start, 1, s - start + 1, fp);
129 fcfdd0a1 2024-03-30 thomas break;
130 fcfdd0a1 2024-03-30 thomas }
131 fcfdd0a1 2024-03-30 thomas start = s + 1;
132 94a3f4e9 2024-03-30 thomas break;
133 fcfdd0a1 2024-03-30 thomas
134 fcfdd0a1 2024-03-30 thomas case UTF8_REJECT:
135 fcfdd0a1 2024-03-30 thomas /* bad UTF-8 sequence; try to recover */
136 fcfdd0a1 2024-03-30 thomas fputs("\\uFFFD", fp);
137 fcfdd0a1 2024-03-30 thomas state = UTF8_ACCEPT;
138 fcfdd0a1 2024-03-30 thomas start = s + 1;
139 94a3f4e9 2024-03-30 thomas break;
140 94a3f4e9 2024-03-30 thomas }
141 94a3f4e9 2024-03-30 thomas }
142 94a3f4e9 2024-03-30 thomas }
143 94a3f4e9 2024-03-30 thomas
144 94a3f4e9 2024-03-30 thomas static void
145 94a3f4e9 2024-03-30 thomas json_field(FILE *fp, const char *key, const char *val, int comma)
146 94a3f4e9 2024-03-30 thomas {
147 94a3f4e9 2024-03-30 thomas fprintf(fp, "\"%s\":\"", key);
148 94a3f4e9 2024-03-30 thomas escape(fp, val);
149 94a3f4e9 2024-03-30 thomas fprintf(fp, "\"%s", comma ? "," : "");
150 94a3f4e9 2024-03-30 thomas }
151 94a3f4e9 2024-03-30 thomas
152 9c485a11 2024-03-30 thomas static void
153 9c485a11 2024-03-30 thomas json_author(FILE *fp, const char *type, char *address, int comma)
154 9c485a11 2024-03-30 thomas {
155 9c485a11 2024-03-30 thomas char *gt, *lt, *at, *email, *endname;
156 9c485a11 2024-03-30 thomas
157 9c485a11 2024-03-30 thomas fprintf(fp, "\"%s\":{", type);
158 9c485a11 2024-03-30 thomas
159 9c485a11 2024-03-30 thomas gt = strchr(address, '<');
160 9c485a11 2024-03-30 thomas if (gt != NULL) {
161 9c485a11 2024-03-30 thomas /* long format, e.g. "Omar Polo <op@openbsd.org>" */
162 9c485a11 2024-03-30 thomas
163 9c485a11 2024-03-30 thomas json_field(fp, "full", address, 1);
164 9c485a11 2024-03-30 thomas
165 9c485a11 2024-03-30 thomas endname = gt;
166 9c485a11 2024-03-30 thomas while (endname > address && endname[-1] == ' ')
167 9c485a11 2024-03-30 thomas endname--;
168 9c485a11 2024-03-30 thomas
169 9c485a11 2024-03-30 thomas *endname = '\0';
170 9c485a11 2024-03-30 thomas json_field(fp, "name", address, 1);
171 9c485a11 2024-03-30 thomas
172 9c485a11 2024-03-30 thomas email = gt + 1;
173 9c485a11 2024-03-30 thomas lt = strchr(email, '>');
174 9c485a11 2024-03-30 thomas if (lt)
175 9c485a11 2024-03-30 thomas *lt = '\0';
176 9c485a11 2024-03-30 thomas
177 9c485a11 2024-03-30 thomas json_field(fp, "mail", email, 1);
178 9c485a11 2024-03-30 thomas
179 9c485a11 2024-03-30 thomas at = strchr(email, '@');
180 9c485a11 2024-03-30 thomas if (at)
181 9c485a11 2024-03-30 thomas *at = '\0';
182 9c485a11 2024-03-30 thomas
183 9c485a11 2024-03-30 thomas json_field(fp, "user", email, 0);
184 9c485a11 2024-03-30 thomas } else {
185 9c485a11 2024-03-30 thomas /* short format only shows the username */
186 9c485a11 2024-03-30 thomas json_field(fp, "user", address, 0);
187 9c485a11 2024-03-30 thomas }
188 9c485a11 2024-03-30 thomas
189 9c485a11 2024-03-30 thomas fprintf(fp, "}%s", comma ? "," : "");
190 9c485a11 2024-03-30 thomas }
191 9c485a11 2024-03-30 thomas
192 94a3f4e9 2024-03-30 thomas static int
193 4808d170 2024-03-30 thomas jsonify_branch_rm(FILE *fp, char *line)
194 4808d170 2024-03-30 thomas {
195 4808d170 2024-03-30 thomas char *ref, *id;
196 4808d170 2024-03-30 thomas
197 4808d170 2024-03-30 thomas line = strchr(line, ' ');
198 4808d170 2024-03-30 thomas if (line == NULL)
199 4808d170 2024-03-30 thomas errx(1, "invalid branch rm line");
200 4808d170 2024-03-30 thomas line += strspn(line, " ");
201 4808d170 2024-03-30 thomas
202 4808d170 2024-03-30 thomas ref = line;
203 4808d170 2024-03-30 thomas
204 4808d170 2024-03-30 thomas line = strchr(line, ':');
205 4808d170 2024-03-30 thomas if (line == NULL)
206 4808d170 2024-03-30 thomas errx(1, "invalid branch rm line");
207 4808d170 2024-03-30 thomas *line++ = '\0';
208 4808d170 2024-03-30 thomas id = line + strspn(line, " ");
209 4808d170 2024-03-30 thomas
210 4808d170 2024-03-30 thomas fputc('{', fp);
211 4808d170 2024-03-30 thomas json_field(fp, "type", "branch-deleted", 1);
212 4808d170 2024-03-30 thomas json_field(fp, "ref", ref, 1);
213 4808d170 2024-03-30 thomas json_field(fp, "id", id, 0);
214 4808d170 2024-03-30 thomas fputc('}', fp);
215 4808d170 2024-03-30 thomas
216 4808d170 2024-03-30 thomas return 0;
217 4808d170 2024-03-30 thomas }
218 4808d170 2024-03-30 thomas
219 4808d170 2024-03-30 thomas static int
220 20d96a10 2024-03-30 thomas jsonify_commit_short(FILE *fp, char *line)
221 94a3f4e9 2024-03-30 thomas {
222 94a3f4e9 2024-03-30 thomas char *t, *date, *id, *author, *message;
223 94a3f4e9 2024-03-30 thomas
224 20d96a10 2024-03-30 thomas t = line;
225 20d96a10 2024-03-30 thomas date = t;
226 20d96a10 2024-03-30 thomas if ((t = strchr(t, ' ')) == NULL)
227 20d96a10 2024-03-30 thomas errx(1, "malformed line");
228 20d96a10 2024-03-30 thomas *t++ = '\0';
229 94a3f4e9 2024-03-30 thomas
230 20d96a10 2024-03-30 thomas id = t;
231 20d96a10 2024-03-30 thomas if ((t = strchr(t, ' ')) == NULL)
232 20d96a10 2024-03-30 thomas errx(1, "malformed line");
233 20d96a10 2024-03-30 thomas *t++ = '\0';
234 94a3f4e9 2024-03-30 thomas
235 20d96a10 2024-03-30 thomas author = t;
236 20d96a10 2024-03-30 thomas if ((t = strchr(t, ' ')) == NULL)
237 20d96a10 2024-03-30 thomas errx(1, "malformed line");
238 20d96a10 2024-03-30 thomas *t++ = '\0';
239 94a3f4e9 2024-03-30 thomas
240 20d96a10 2024-03-30 thomas message = t;
241 94a3f4e9 2024-03-30 thomas
242 71b7e0f5 2024-03-30 thomas fprintf(fp, "{\"type\":\"commit\",\"short\":true,");
243 20d96a10 2024-03-30 thomas json_field(fp, "id", id, 1);
244 20d96a10 2024-03-30 thomas json_author(fp, "committer", author, 1);
245 20d96a10 2024-03-30 thomas json_field(fp, "date", date, 1);
246 20d96a10 2024-03-30 thomas json_field(fp, "short_message", message, 0);
247 20d96a10 2024-03-30 thomas fprintf(fp, "}");
248 94a3f4e9 2024-03-30 thomas
249 94a3f4e9 2024-03-30 thomas return 0;
250 94a3f4e9 2024-03-30 thomas }
251 94a3f4e9 2024-03-30 thomas
252 94a3f4e9 2024-03-30 thomas static int
253 20d96a10 2024-03-30 thomas jsonify_commit(FILE *fp, char **line, ssize_t *linesize)
254 94a3f4e9 2024-03-30 thomas {
255 94a3f4e9 2024-03-30 thomas const char *errstr;
256 9c485a11 2024-03-30 thomas char *author = NULL;
257 94a3f4e9 2024-03-30 thomas char *l;
258 94a3f4e9 2024-03-30 thomas ssize_t linelen;
259 94a3f4e9 2024-03-30 thomas int parent = 0;
260 94a3f4e9 2024-03-30 thomas int msglen = 0, msgwrote = 0;
261 20d96a10 2024-03-30 thomas int done = 0;
262 94a3f4e9 2024-03-30 thomas enum {
263 94a3f4e9 2024-03-30 thomas P_FROM,
264 94a3f4e9 2024-03-30 thomas P_VIA,
265 94a3f4e9 2024-03-30 thomas P_DATE,
266 94a3f4e9 2024-03-30 thomas P_PARENT,
267 94a3f4e9 2024-03-30 thomas P_MSGLEN,
268 94a3f4e9 2024-03-30 thomas P_MSG,
269 94a3f4e9 2024-03-30 thomas P_DST,
270 94a3f4e9 2024-03-30 thomas P_SUM,
271 20d96a10 2024-03-30 thomas } phase = P_FROM;
272 94a3f4e9 2024-03-30 thomas
273 20d96a10 2024-03-30 thomas l = *line;
274 20d96a10 2024-03-30 thomas if (strncmp(l, "commit ", 7) != 0)
275 20d96a10 2024-03-30 thomas errx(1, "%s: unexpected line: %s", __func__, l);
276 20d96a10 2024-03-30 thomas l += 7;
277 71b7e0f5 2024-03-30 thomas fprintf(fp, "{\"type\":\"commit\",\"short\":false,");
278 20d96a10 2024-03-30 thomas json_field(fp, "id", l, 1);
279 94a3f4e9 2024-03-30 thomas
280 20d96a10 2024-03-30 thomas while (!done) {
281 20d96a10 2024-03-30 thomas if ((linelen = getline(line, linesize, stdin)) == -1)
282 20d96a10 2024-03-30 thomas break;
283 94a3f4e9 2024-03-30 thomas
284 20d96a10 2024-03-30 thomas if ((*line)[linelen - 1] == '\n')
285 20d96a10 2024-03-30 thomas (*line)[--linelen] = '\0';
286 94a3f4e9 2024-03-30 thomas
287 20d96a10 2024-03-30 thomas l = *line;
288 20d96a10 2024-03-30 thomas switch (phase) {
289 94a3f4e9 2024-03-30 thomas case P_FROM:
290 94a3f4e9 2024-03-30 thomas if (strncmp(l, "from: ", 6) != 0)
291 94a3f4e9 2024-03-30 thomas errx(1, "unexpected from line");
292 94a3f4e9 2024-03-30 thomas l += 6;
293 9c485a11 2024-03-30 thomas
294 9c485a11 2024-03-30 thomas author = strdup(l);
295 9c485a11 2024-03-30 thomas if (author == NULL)
296 9c485a11 2024-03-30 thomas err(1, "strdup");
297 9c485a11 2024-03-30 thomas
298 9c485a11 2024-03-30 thomas json_author(fp, "author", l, 1);
299 9c485a11 2024-03-30 thomas
300 94a3f4e9 2024-03-30 thomas phase = P_VIA;
301 94a3f4e9 2024-03-30 thomas break;
302 94a3f4e9 2024-03-30 thomas
303 94a3f4e9 2024-03-30 thomas case P_VIA:
304 94a3f4e9 2024-03-30 thomas /* optional */
305 94a3f4e9 2024-03-30 thomas if (!strncmp(l, "via: ", 5)) {
306 94a3f4e9 2024-03-30 thomas l += 5;
307 9c485a11 2024-03-30 thomas json_author(fp, "committer", l, 1);
308 94a3f4e9 2024-03-30 thomas phase = P_DATE;
309 94a3f4e9 2024-03-30 thomas break;
310 94a3f4e9 2024-03-30 thomas }
311 9c485a11 2024-03-30 thomas
312 9c485a11 2024-03-30 thomas if (author == NULL) /* impossible */
313 9c485a11 2024-03-30 thomas err(1, "from not specified");
314 9c485a11 2024-03-30 thomas json_author(fp, "committer", author, 1);
315 9c485a11 2024-03-30 thomas free(author);
316 9c485a11 2024-03-30 thomas author = NULL;
317 9c485a11 2024-03-30 thomas
318 94a3f4e9 2024-03-30 thomas phase = P_DATE;
319 94a3f4e9 2024-03-30 thomas /* fallthrough */
320 94a3f4e9 2024-03-30 thomas
321 94a3f4e9 2024-03-30 thomas case P_DATE:
322 94a3f4e9 2024-03-30 thomas /* optional */
323 94a3f4e9 2024-03-30 thomas if (!strncmp(l, "date: ", 6)) {
324 94a3f4e9 2024-03-30 thomas l += 6;
325 94a3f4e9 2024-03-30 thomas json_field(fp, "date", l, 1);
326 94a3f4e9 2024-03-30 thomas phase = P_PARENT;
327 94a3f4e9 2024-03-30 thomas break;
328 94a3f4e9 2024-03-30 thomas }
329 94a3f4e9 2024-03-30 thomas phase = P_PARENT;
330 94a3f4e9 2024-03-30 thomas /* fallthough */
331 94a3f4e9 2024-03-30 thomas
332 94a3f4e9 2024-03-30 thomas case P_PARENT:
333 94a3f4e9 2024-03-30 thomas /* optional - more than one */
334 94a3f4e9 2024-03-30 thomas if (!strncmp(l, "parent ", 7)) {
335 94a3f4e9 2024-03-30 thomas l += 7;
336 94a3f4e9 2024-03-30 thomas l += strcspn(l, ":");
337 94a3f4e9 2024-03-30 thomas l += strspn(l, " ");
338 94a3f4e9 2024-03-30 thomas
339 94a3f4e9 2024-03-30 thomas if (parent == 0) {
340 94a3f4e9 2024-03-30 thomas parent = 1;
341 94a3f4e9 2024-03-30 thomas fprintf(fp, "\"parents\":[");
342 94a3f4e9 2024-03-30 thomas }
343 94a3f4e9 2024-03-30 thomas
344 94a3f4e9 2024-03-30 thomas fputc('"', fp);
345 94a3f4e9 2024-03-30 thomas escape(fp, l);
346 94a3f4e9 2024-03-30 thomas fputc('"', fp);
347 94a3f4e9 2024-03-30 thomas
348 94a3f4e9 2024-03-30 thomas break;
349 94a3f4e9 2024-03-30 thomas }
350 94a3f4e9 2024-03-30 thomas if (parent != 0) {
351 94a3f4e9 2024-03-30 thomas fprintf(fp, "],");
352 94a3f4e9 2024-03-30 thomas parent = 0;
353 94a3f4e9 2024-03-30 thomas }
354 94a3f4e9 2024-03-30 thomas phase = P_MSGLEN;
355 94a3f4e9 2024-03-30 thomas /* fallthrough */
356 94a3f4e9 2024-03-30 thomas
357 94a3f4e9 2024-03-30 thomas case P_MSGLEN:
358 94a3f4e9 2024-03-30 thomas if (strncmp(l, "messagelen: ", 12) != 0)
359 94a3f4e9 2024-03-30 thomas errx(1, "unexpected messagelen line");
360 94a3f4e9 2024-03-30 thomas l += 12;
361 94a3f4e9 2024-03-30 thomas msglen = strtonum(l, 1, INT_MAX, &errstr);
362 94a3f4e9 2024-03-30 thomas if (errstr)
363 94a3f4e9 2024-03-30 thomas errx(1, "message len is %s: %s", errstr, l);
364 94a3f4e9 2024-03-30 thomas
365 94a3f4e9 2024-03-30 thomas phase = P_MSG;
366 94a3f4e9 2024-03-30 thomas break;
367 94a3f4e9 2024-03-30 thomas
368 94a3f4e9 2024-03-30 thomas case P_MSG:
369 94a3f4e9 2024-03-30 thomas /*
370 94a3f4e9 2024-03-30 thomas * The commit message is indented with one extra
371 94a3f4e9 2024-03-30 thomas * space which is not accounted for in messagelen,
372 94a3f4e9 2024-03-30 thomas * but we also strip the trailing \n so that
373 94a3f4e9 2024-03-30 thomas * accounts for it.
374 94a3f4e9 2024-03-30 thomas *
375 94a3f4e9 2024-03-30 thomas * Since we read line-by-line and there is always
376 94a3f4e9 2024-03-30 thomas * a \n added at the end of the message,
377 94a3f4e9 2024-03-30 thomas * tolerate one byte less than advertised.
378 94a3f4e9 2024-03-30 thomas */
379 94a3f4e9 2024-03-30 thomas if (*l == ' ') {
380 9c485a11 2024-03-30 thomas l++; /* skip leading space */
381 9c485a11 2024-03-30 thomas linelen--;
382 94a3f4e9 2024-03-30 thomas
383 9c485a11 2024-03-30 thomas if (msgwrote == 0 && linelen != 0) {
384 9c485a11 2024-03-30 thomas json_field(fp, "short_message", l, 1);
385 9c485a11 2024-03-30 thomas fprintf(fp, "\"message\":\"");
386 9c485a11 2024-03-30 thomas escape(fp, l);
387 9c485a11 2024-03-30 thomas escape(fp, "\n");
388 9c485a11 2024-03-30 thomas msgwrote += linelen;
389 9c485a11 2024-03-30 thomas } else if (msgwrote != 0) {
390 9c485a11 2024-03-30 thomas escape(fp, l);
391 94a3f4e9 2024-03-30 thomas escape(fp, "\n");
392 9c485a11 2024-03-30 thomas }
393 94a3f4e9 2024-03-30 thomas }
394 9c485a11 2024-03-30 thomas msglen -= linelen + 1;
395 94a3f4e9 2024-03-30 thomas if (msglen <= 1) {
396 94a3f4e9 2024-03-30 thomas fprintf(fp, "\",");
397 94a3f4e9 2024-03-30 thomas msgwrote = 0;
398 94a3f4e9 2024-03-30 thomas phase = P_DST;
399 94a3f4e9 2024-03-30 thomas }
400 94a3f4e9 2024-03-30 thomas break;
401 94a3f4e9 2024-03-30 thomas
402 94a3f4e9 2024-03-30 thomas case P_DST:
403 94a3f4e9 2024-03-30 thomas /* XXX: ignore the diffstat for now */
404 20d96a10 2024-03-30 thomas if (*l == '\0') {
405 94a3f4e9 2024-03-30 thomas fprintf(fp, "\"diffstat\":{},");
406 94a3f4e9 2024-03-30 thomas phase = P_SUM;
407 94a3f4e9 2024-03-30 thomas break;
408 94a3f4e9 2024-03-30 thomas }
409 94a3f4e9 2024-03-30 thomas break;
410 94a3f4e9 2024-03-30 thomas
411 94a3f4e9 2024-03-30 thomas case P_SUM:
412 94a3f4e9 2024-03-30 thomas /* XXX: ignore the sum of changes for now */
413 94a3f4e9 2024-03-30 thomas fprintf(fp, "\"changes\":{}}");
414 20d96a10 2024-03-30 thomas done = 1;
415 94a3f4e9 2024-03-30 thomas break;
416 94a3f4e9 2024-03-30 thomas
417 94a3f4e9 2024-03-30 thomas default:
418 20d96a10 2024-03-30 thomas /* unreachable */
419 20d96a10 2024-03-30 thomas errx(1, "unexpected line: %s", *line);
420 94a3f4e9 2024-03-30 thomas }
421 94a3f4e9 2024-03-30 thomas }
422 94a3f4e9 2024-03-30 thomas if (ferror(stdin))
423 94a3f4e9 2024-03-30 thomas err(1, "getline");
424 20d96a10 2024-03-30 thomas if (!done)
425 94a3f4e9 2024-03-30 thomas errx(1, "unexpected EOF");
426 20d96a10 2024-03-30 thomas
427 20d96a10 2024-03-30 thomas return 0;
428 20d96a10 2024-03-30 thomas }
429 20d96a10 2024-03-30 thomas
430 20d96a10 2024-03-30 thomas static int
431 e0d15dee 2024-03-30 thomas jsonify_tag(FILE *fp, char **line, ssize_t *linesize)
432 e0d15dee 2024-03-30 thomas {
433 e0d15dee 2024-03-30 thomas const char *errstr;
434 e0d15dee 2024-03-30 thomas char *l;
435 e0d15dee 2024-03-30 thomas ssize_t linelen;
436 e0d15dee 2024-03-30 thomas int msglen = 0, msgwrote = 0;
437 e0d15dee 2024-03-30 thomas int done = 0;
438 e0d15dee 2024-03-30 thomas enum {
439 e0d15dee 2024-03-30 thomas P_FROM,
440 e0d15dee 2024-03-30 thomas P_DATE,
441 e0d15dee 2024-03-30 thomas P_OBJECT,
442 e0d15dee 2024-03-30 thomas P_MSGLEN,
443 e0d15dee 2024-03-30 thomas P_MSG,
444 e0d15dee 2024-03-30 thomas } phase = P_FROM;
445 e0d15dee 2024-03-30 thomas
446 e0d15dee 2024-03-30 thomas l = *line;
447 e0d15dee 2024-03-30 thomas if (strncmp(l, "tag ", 4) != 0)
448 e0d15dee 2024-03-30 thomas errx(1, "%s: unexpected line: %s", __func__, l);
449 e0d15dee 2024-03-30 thomas l += 4;
450 e0d15dee 2024-03-30 thomas
451 e0d15dee 2024-03-30 thomas fputc('{', fp);
452 e0d15dee 2024-03-30 thomas json_field(fp, "type", "tag", 1);
453 e0d15dee 2024-03-30 thomas json_field(fp, "tag", l, 1);
454 e0d15dee 2024-03-30 thomas
455 e0d15dee 2024-03-30 thomas while (!done) {
456 e0d15dee 2024-03-30 thomas if ((linelen = getline(line, linesize, stdin)) == -1)
457 e0d15dee 2024-03-30 thomas break;
458 e0d15dee 2024-03-30 thomas
459 e0d15dee 2024-03-30 thomas if ((*line)[linelen - 1] == '\n')
460 e0d15dee 2024-03-30 thomas (*line)[--linelen] = '\0';
461 e0d15dee 2024-03-30 thomas
462 e0d15dee 2024-03-30 thomas l = *line;
463 e0d15dee 2024-03-30 thomas switch (phase) {
464 e0d15dee 2024-03-30 thomas case P_FROM:
465 e0d15dee 2024-03-30 thomas if (strncmp(l, "from: ", 6) != 0)
466 e0d15dee 2024-03-30 thomas errx(1, "unexpected from line");
467 e0d15dee 2024-03-30 thomas l += 6;
468 e0d15dee 2024-03-30 thomas
469 e0d15dee 2024-03-30 thomas json_author(fp, "tagger", l, 1);
470 e0d15dee 2024-03-30 thomas
471 e0d15dee 2024-03-30 thomas phase = P_DATE;
472 e0d15dee 2024-03-30 thomas break;
473 e0d15dee 2024-03-30 thomas
474 e0d15dee 2024-03-30 thomas case P_DATE:
475 e0d15dee 2024-03-30 thomas /* optional */
476 e0d15dee 2024-03-30 thomas if (!strncmp(l, "date: ", 6)) {
477 e0d15dee 2024-03-30 thomas l += 6;
478 e0d15dee 2024-03-30 thomas json_field(fp, "date", l, 1);
479 e0d15dee 2024-03-30 thomas phase = P_OBJECT;
480 e0d15dee 2024-03-30 thomas break;
481 e0d15dee 2024-03-30 thomas }
482 e0d15dee 2024-03-30 thomas phase = P_OBJECT;
483 e0d15dee 2024-03-30 thomas /* fallthough */
484 e0d15dee 2024-03-30 thomas
485 e0d15dee 2024-03-30 thomas case P_OBJECT:
486 e0d15dee 2024-03-30 thomas /* optional */
487 e0d15dee 2024-03-30 thomas if (!strncmp(l, "object: ", 8)) {
488 e0d15dee 2024-03-30 thomas char *type, *id;
489 e0d15dee 2024-03-30 thomas
490 e0d15dee 2024-03-30 thomas l += 8;
491 e0d15dee 2024-03-30 thomas type = l;
492 e0d15dee 2024-03-30 thomas id = strchr(l, ' ');
493 e0d15dee 2024-03-30 thomas if (id == NULL)
494 e0d15dee 2024-03-30 thomas errx(1, "malformed tag object line");
495 e0d15dee 2024-03-30 thomas *id++ = '\0';
496 e0d15dee 2024-03-30 thomas
497 e0d15dee 2024-03-30 thomas fputs("\"object\":{", fp);
498 e0d15dee 2024-03-30 thomas json_field(fp, "type", type, 1);
499 e0d15dee 2024-03-30 thomas json_field(fp, "id", id, 0);
500 e0d15dee 2024-03-30 thomas fputs("},", fp);
501 e0d15dee 2024-03-30 thomas
502 e0d15dee 2024-03-30 thomas phase = P_MSGLEN;
503 e0d15dee 2024-03-30 thomas break;
504 e0d15dee 2024-03-30 thomas }
505 e0d15dee 2024-03-30 thomas phase = P_MSGLEN;
506 e0d15dee 2024-03-30 thomas /* fallthrough */
507 e0d15dee 2024-03-30 thomas
508 e0d15dee 2024-03-30 thomas case P_MSGLEN:
509 e0d15dee 2024-03-30 thomas if (strncmp(l, "messagelen: ", 12) != 0)
510 e0d15dee 2024-03-30 thomas errx(1, "unexpected messagelen line");
511 e0d15dee 2024-03-30 thomas l += 12;
512 e0d15dee 2024-03-30 thomas msglen = strtonum(l, 1, INT_MAX, &errstr);
513 e0d15dee 2024-03-30 thomas if (errstr)
514 e0d15dee 2024-03-30 thomas errx(1, "message len is %s: %s", errstr, l);
515 e0d15dee 2024-03-30 thomas
516 e0d15dee 2024-03-30 thomas msglen++;
517 e0d15dee 2024-03-30 thomas
518 e0d15dee 2024-03-30 thomas phase = P_MSG;
519 e0d15dee 2024-03-30 thomas break;
520 e0d15dee 2024-03-30 thomas
521 e0d15dee 2024-03-30 thomas case P_MSG:
522 e0d15dee 2024-03-30 thomas if (*l == ' ') {
523 e0d15dee 2024-03-30 thomas l++; /* skip leading space */
524 e0d15dee 2024-03-30 thomas linelen--;
525 e0d15dee 2024-03-30 thomas
526 e0d15dee 2024-03-30 thomas if (msgwrote == 0 && linelen != 0) {
527 e0d15dee 2024-03-30 thomas fprintf(fp, "\"message\":\"");
528 e0d15dee 2024-03-30 thomas escape(fp, l);
529 e0d15dee 2024-03-30 thomas escape(fp, "\n");
530 e0d15dee 2024-03-30 thomas msgwrote += linelen;
531 e0d15dee 2024-03-30 thomas } else if (msgwrote != 0) {
532 e0d15dee 2024-03-30 thomas escape(fp, l);
533 e0d15dee 2024-03-30 thomas escape(fp, "\n");
534 e0d15dee 2024-03-30 thomas }
535 e0d15dee 2024-03-30 thomas }
536 e0d15dee 2024-03-30 thomas msglen -= linelen + 1;
537 e0d15dee 2024-03-30 thomas if (msglen <= 0) {
538 e0d15dee 2024-03-30 thomas fprintf(fp, "\"");
539 e0d15dee 2024-03-30 thomas msgwrote = 0;
540 e0d15dee 2024-03-30 thomas done = 1;
541 e0d15dee 2024-03-30 thomas break;
542 e0d15dee 2024-03-30 thomas }
543 e0d15dee 2024-03-30 thomas break;
544 e0d15dee 2024-03-30 thomas
545 e0d15dee 2024-03-30 thomas default:
546 e0d15dee 2024-03-30 thomas /* unreachable */
547 e0d15dee 2024-03-30 thomas errx(1, "unexpected line: %s", *line);
548 e0d15dee 2024-03-30 thomas }
549 e0d15dee 2024-03-30 thomas }
550 e0d15dee 2024-03-30 thomas if (ferror(stdin))
551 e0d15dee 2024-03-30 thomas err(1, "getline");
552 e0d15dee 2024-03-30 thomas if (!done)
553 e0d15dee 2024-03-30 thomas errx(1, "unexpected EOF");
554 e0d15dee 2024-03-30 thomas fputc('}', fp);
555 e0d15dee 2024-03-30 thomas
556 e0d15dee 2024-03-30 thomas return 0;
557 e0d15dee 2024-03-30 thomas }
558 e0d15dee 2024-03-30 thomas
559 e0d15dee 2024-03-30 thomas static int
560 20d96a10 2024-03-30 thomas jsonify(FILE *fp)
561 20d96a10 2024-03-30 thomas {
562 20d96a10 2024-03-30 thomas char *line = NULL;
563 20d96a10 2024-03-30 thomas size_t linesize = 0;
564 20d96a10 2024-03-30 thomas ssize_t linelen;
565 20d96a10 2024-03-30 thomas int needcomma = 0;
566 20d96a10 2024-03-30 thomas
567 20d96a10 2024-03-30 thomas fprintf(fp, "{\"notifications\":[");
568 20d96a10 2024-03-30 thomas while ((linelen = getline(&line, &linesize, stdin)) != -1) {
569 20d96a10 2024-03-30 thomas if (line[linelen - 1] == '\n')
570 20d96a10 2024-03-30 thomas line[--linelen] = '\0';
571 94a3f4e9 2024-03-30 thomas
572 20d96a10 2024-03-30 thomas if (*line == '\0')
573 20d96a10 2024-03-30 thomas continue;
574 20d96a10 2024-03-30 thomas
575 20d96a10 2024-03-30 thomas if (needcomma)
576 20d96a10 2024-03-30 thomas fputc(',', fp);
577 20d96a10 2024-03-30 thomas needcomma = 1;
578 20d96a10 2024-03-30 thomas
579 4808d170 2024-03-30 thomas if (strncmp(line, "Removed refs/heads/", 19) == 0) {
580 4808d170 2024-03-30 thomas if (jsonify_branch_rm(fp, line) == -1)
581 4808d170 2024-03-30 thomas err(1, "jsonify_branch_rm");
582 4808d170 2024-03-30 thomas continue;
583 4808d170 2024-03-30 thomas }
584 4808d170 2024-03-30 thomas
585 20d96a10 2024-03-30 thomas if (strncmp(line, "commit ", 7) == 0) {
586 20d96a10 2024-03-30 thomas if (jsonify_commit(fp, &line, &linesize) == -1)
587 20d96a10 2024-03-30 thomas err(1, "jsonify_commit");
588 20d96a10 2024-03-30 thomas continue;
589 20d96a10 2024-03-30 thomas }
590 20d96a10 2024-03-30 thomas
591 20d96a10 2024-03-30 thomas if (*line >= '0' && *line <= '9') {
592 20d96a10 2024-03-30 thomas if (jsonify_commit_short(fp, line) == -1)
593 20d96a10 2024-03-30 thomas err(1, "jsonify_commit_short");
594 20d96a10 2024-03-30 thomas continue;
595 20d96a10 2024-03-30 thomas }
596 20d96a10 2024-03-30 thomas
597 e0d15dee 2024-03-30 thomas if (strncmp(line, "tag ", 4) == 0) {
598 e0d15dee 2024-03-30 thomas if (jsonify_tag(fp, &line, &linesize) == -1)
599 e0d15dee 2024-03-30 thomas err(1, "jsonify_tag");
600 e0d15dee 2024-03-30 thomas continue;
601 e0d15dee 2024-03-30 thomas }
602 e0d15dee 2024-03-30 thomas
603 20d96a10 2024-03-30 thomas errx(1, "unexpected line: %s", line);
604 20d96a10 2024-03-30 thomas }
605 20d96a10 2024-03-30 thomas if (ferror(stdin))
606 20d96a10 2024-03-30 thomas err(1, "getline");
607 94a3f4e9 2024-03-30 thomas fprintf(fp, "]}");
608 94a3f4e9 2024-03-30 thomas
609 94a3f4e9 2024-03-30 thomas return 0;
610 94a3f4e9 2024-03-30 thomas }
611 94a3f4e9 2024-03-30 thomas
612 94a3f4e9 2024-03-30 thomas static char *
613 94a3f4e9 2024-03-30 thomas basic_auth(const char *username, const char *password)
614 94a3f4e9 2024-03-30 thomas {
615 94a3f4e9 2024-03-30 thomas char *tmp;
616 94a3f4e9 2024-03-30 thomas int len;
617 94a3f4e9 2024-03-30 thomas
618 94a3f4e9 2024-03-30 thomas len = asprintf(&tmp, "%s:%s", username, password);
619 94a3f4e9 2024-03-30 thomas if (len == -1)
620 94a3f4e9 2024-03-30 thomas err(1, "asprintf");
621 94a3f4e9 2024-03-30 thomas
622 94a3f4e9 2024-03-30 thomas /* XXX base64-ify */
623 94a3f4e9 2024-03-30 thomas return tmp;
624 94a3f4e9 2024-03-30 thomas }
625 94a3f4e9 2024-03-30 thomas
626 94a3f4e9 2024-03-30 thomas static inline int
627 94a3f4e9 2024-03-30 thomas bufio2poll(struct bufio *bio)
628 94a3f4e9 2024-03-30 thomas {
629 94a3f4e9 2024-03-30 thomas int f, ret = 0;
630 94a3f4e9 2024-03-30 thomas
631 94a3f4e9 2024-03-30 thomas f = bufio_ev(bio);
632 94a3f4e9 2024-03-30 thomas if (f & BUFIO_WANT_READ)
633 94a3f4e9 2024-03-30 thomas ret |= POLLIN;
634 94a3f4e9 2024-03-30 thomas if (f & BUFIO_WANT_WRITE)
635 94a3f4e9 2024-03-30 thomas ret |= POLLOUT;
636 94a3f4e9 2024-03-30 thomas return ret;
637 94a3f4e9 2024-03-30 thomas }
638 94a3f4e9 2024-03-30 thomas
639 94a3f4e9 2024-03-30 thomas int
640 94a3f4e9 2024-03-30 thomas main(int argc, char **argv)
641 94a3f4e9 2024-03-30 thomas {
642 94a3f4e9 2024-03-30 thomas FILE *tmpfp;
643 94a3f4e9 2024-03-30 thomas struct bufio bio;
644 94a3f4e9 2024-03-30 thomas struct pollfd pfd;
645 94a3f4e9 2024-03-30 thomas struct timespec timeout;
646 94a3f4e9 2024-03-30 thomas const char *username;
647 94a3f4e9 2024-03-30 thomas const char *password;
648 94a3f4e9 2024-03-30 thomas const char *timeoutstr;
649 94a3f4e9 2024-03-30 thomas const char *errstr;
650 94a3f4e9 2024-03-30 thomas const char *host = NULL, *port = NULL, *path = NULL;
651 94a3f4e9 2024-03-30 thomas char *auth, *line, *spc;
652 94a3f4e9 2024-03-30 thomas size_t len;
653 94a3f4e9 2024-03-30 thomas ssize_t r;
654 94a3f4e9 2024-03-30 thomas off_t paylen;
655 94a3f4e9 2024-03-30 thomas int tls = 0;
656 94a3f4e9 2024-03-30 thomas int response_code = 0, done = 0;
657 94a3f4e9 2024-03-30 thomas int ch, flags, ret, nonstd = 0;
658 94a3f4e9 2024-03-30 thomas
659 94a3f4e9 2024-03-30 thomas #ifndef PROFILE
660 94a3f4e9 2024-03-30 thomas if (pledge("stdio rpath tmppath dns inet", NULL) == -1)
661 94a3f4e9 2024-03-30 thomas err(1, "pledge");
662 94a3f4e9 2024-03-30 thomas #endif
663 94a3f4e9 2024-03-30 thomas
664 94a3f4e9 2024-03-30 thomas while ((ch = getopt(argc, argv, "ch:p:")) != -1) {
665 94a3f4e9 2024-03-30 thomas switch (ch) {
666 94a3f4e9 2024-03-30 thomas case 'c':
667 94a3f4e9 2024-03-30 thomas tls = 1;
668 94a3f4e9 2024-03-30 thomas break;
669 94a3f4e9 2024-03-30 thomas case 'h':
670 94a3f4e9 2024-03-30 thomas host = optarg;
671 94a3f4e9 2024-03-30 thomas break;
672 94a3f4e9 2024-03-30 thomas case 'p':
673 94a3f4e9 2024-03-30 thomas port = optarg;
674 94a3f4e9 2024-03-30 thomas break;
675 94a3f4e9 2024-03-30 thomas default:
676 94a3f4e9 2024-03-30 thomas usage();
677 94a3f4e9 2024-03-30 thomas }
678 94a3f4e9 2024-03-30 thomas }
679 94a3f4e9 2024-03-30 thomas argc -= optind;
680 94a3f4e9 2024-03-30 thomas argv += optind;
681 94a3f4e9 2024-03-30 thomas
682 94a3f4e9 2024-03-30 thomas if (host == NULL || argc != 1)
683 94a3f4e9 2024-03-30 thomas usage();
684 94a3f4e9 2024-03-30 thomas if (tls && port == NULL)
685 94a3f4e9 2024-03-30 thomas port = "443";
686 94a3f4e9 2024-03-30 thomas path = argv[0];
687 94a3f4e9 2024-03-30 thomas
688 94a3f4e9 2024-03-30 thomas username = getenv("GOT_NOTIFY_HTTP_USER");
689 94a3f4e9 2024-03-30 thomas password = getenv("GOT_NOTIFY_HTTP_PASS");
690 94a3f4e9 2024-03-30 thomas if ((username != NULL && password == NULL) ||
691 94a3f4e9 2024-03-30 thomas (username == NULL && password != NULL))
692 94a3f4e9 2024-03-30 thomas errx(1, "username or password are not specified");
693 94a3f4e9 2024-03-30 thomas if (username && *password == '\0')
694 94a3f4e9 2024-03-30 thomas errx(1, "password can't be empty");
695 94a3f4e9 2024-03-30 thomas
696 94a3f4e9 2024-03-30 thomas /* used by the regression test suite */
697 94a3f4e9 2024-03-30 thomas timeoutstr = getenv("GOT_NOTIFY_TIMEOUT");
698 94a3f4e9 2024-03-30 thomas if (timeoutstr) {
699 94a3f4e9 2024-03-30 thomas http_timeout = strtonum(timeoutstr, 0, 600, &errstr);
700 94a3f4e9 2024-03-30 thomas if (errstr != NULL)
701 94a3f4e9 2024-03-30 thomas errx(1, "timeout in seconds is %s: %s",
702 94a3f4e9 2024-03-30 thomas errstr, timeoutstr);
703 94a3f4e9 2024-03-30 thomas }
704 94a3f4e9 2024-03-30 thomas
705 94a3f4e9 2024-03-30 thomas memset(&timeout, 0, sizeof(timeout));
706 94a3f4e9 2024-03-30 thomas timeout.tv_sec = http_timeout;
707 94a3f4e9 2024-03-30 thomas
708 94a3f4e9 2024-03-30 thomas tmpfp = got_opentemp();
709 94a3f4e9 2024-03-30 thomas if (tmpfp == NULL)
710 94a3f4e9 2024-03-30 thomas err(1, "opentemp");
711 94a3f4e9 2024-03-30 thomas
712 20d96a10 2024-03-30 thomas jsonify(tmpfp);
713 94a3f4e9 2024-03-30 thomas
714 94a3f4e9 2024-03-30 thomas paylen = ftello(tmpfp);
715 94a3f4e9 2024-03-30 thomas if (paylen == -1)
716 94a3f4e9 2024-03-30 thomas err(1, "ftello");
717 94a3f4e9 2024-03-30 thomas if (fseeko(tmpfp, 0, SEEK_SET) == -1)
718 94a3f4e9 2024-03-30 thomas err(1, "fseeko");
719 94a3f4e9 2024-03-30 thomas
720 94a3f4e9 2024-03-30 thomas #ifndef PROFILE
721 94a3f4e9 2024-03-30 thomas /* drop tmppath */
722 94a3f4e9 2024-03-30 thomas if (pledge("stdio rpath dns inet", NULL) == -1)
723 94a3f4e9 2024-03-30 thomas err(1, "pledge");
724 94a3f4e9 2024-03-30 thomas #endif
725 94a3f4e9 2024-03-30 thomas
726 94a3f4e9 2024-03-30 thomas memset(&pfd, 0, sizeof(pfd));
727 94a3f4e9 2024-03-30 thomas pfd.fd = dial(host, port);
728 94a3f4e9 2024-03-30 thomas
729 94a3f4e9 2024-03-30 thomas if ((flags = fcntl(pfd.fd, F_GETFL)) == -1)
730 94a3f4e9 2024-03-30 thomas err(1, "fcntl(F_GETFL)");
731 94a3f4e9 2024-03-30 thomas if (fcntl(pfd.fd, F_SETFL, flags | O_NONBLOCK) == -1)
732 94a3f4e9 2024-03-30 thomas err(1, "fcntl(F_SETFL)");
733 94a3f4e9 2024-03-30 thomas
734 94a3f4e9 2024-03-30 thomas if (bufio_init(&bio) == -1)
735 94a3f4e9 2024-03-30 thomas err(1, "bufio_init");
736 94a3f4e9 2024-03-30 thomas bufio_set_fd(&bio, pfd.fd);
737 94a3f4e9 2024-03-30 thomas if (tls && bufio_starttls(&bio, host, 0, NULL, 0, NULL, 0) == -1)
738 94a3f4e9 2024-03-30 thomas err(1, "bufio_starttls");
739 94a3f4e9 2024-03-30 thomas
740 94a3f4e9 2024-03-30 thomas #ifndef PROFILE
741 94a3f4e9 2024-03-30 thomas /* drop rpath dns inet */
742 94a3f4e9 2024-03-30 thomas if (pledge("stdio", NULL) == -1)
743 94a3f4e9 2024-03-30 thomas err(1, "pledge");
744 94a3f4e9 2024-03-30 thomas #endif
745 94a3f4e9 2024-03-30 thomas
746 94a3f4e9 2024-03-30 thomas if ((!tls && strcmp(port, "80") != 0) ||
747 94a3f4e9 2024-03-30 thomas (tls && strcmp(port, "443")) != 0)
748 94a3f4e9 2024-03-30 thomas nonstd = 1;
749 94a3f4e9 2024-03-30 thomas
750 94a3f4e9 2024-03-30 thomas ret = bufio_compose_fmt(&bio,
751 94a3f4e9 2024-03-30 thomas "POST %s HTTP/1.1\r\n"
752 94a3f4e9 2024-03-30 thomas "Host: %s%s%s\r\n"
753 94a3f4e9 2024-03-30 thomas "Content-Type: application/json\r\n"
754 94a3f4e9 2024-03-30 thomas "Content-Length: %lld\r\n"
755 94a3f4e9 2024-03-30 thomas "User-Agent: %s\r\n"
756 94a3f4e9 2024-03-30 thomas "Connection: close\r\n",
757 94a3f4e9 2024-03-30 thomas path, host,
758 94a3f4e9 2024-03-30 thomas nonstd ? ":" : "", nonstd ? port : "",
759 94a3f4e9 2024-03-30 thomas (long long)paylen, USERAGENT);
760 94a3f4e9 2024-03-30 thomas if (ret == -1)
761 94a3f4e9 2024-03-30 thomas err(1, "bufio_compose_fmt");
762 94a3f4e9 2024-03-30 thomas
763 94a3f4e9 2024-03-30 thomas if (username) {
764 94a3f4e9 2024-03-30 thomas auth = basic_auth(username, password);
765 94a3f4e9 2024-03-30 thomas ret = bufio_compose_fmt(&bio, "Authorization: basic %s\r\n",
766 94a3f4e9 2024-03-30 thomas auth);
767 94a3f4e9 2024-03-30 thomas if (ret == -1)
768 94a3f4e9 2024-03-30 thomas err(1, "bufio_compose_fmt");
769 94a3f4e9 2024-03-30 thomas free(auth);
770 94a3f4e9 2024-03-30 thomas }
771 94a3f4e9 2024-03-30 thomas
772 94a3f4e9 2024-03-30 thomas if (bufio_compose(&bio, "\r\n", 2) == -1)
773 94a3f4e9 2024-03-30 thomas err(1, "bufio_compose");
774 94a3f4e9 2024-03-30 thomas
775 94a3f4e9 2024-03-30 thomas while (!done) {
776 94a3f4e9 2024-03-30 thomas struct timespec elapsed, start, stop;
777 94a3f4e9 2024-03-30 thomas char buf[BUFSIZ];
778 94a3f4e9 2024-03-30 thomas
779 94a3f4e9 2024-03-30 thomas pfd.events = bufio2poll(&bio);
780 94a3f4e9 2024-03-30 thomas clock_gettime(CLOCK_MONOTONIC, &start);
781 94a3f4e9 2024-03-30 thomas ret = ppoll(&pfd, 1, &timeout, NULL);
782 94a3f4e9 2024-03-30 thomas if (ret == -1)
783 94a3f4e9 2024-03-30 thomas err(1, "poll");
784 94a3f4e9 2024-03-30 thomas clock_gettime(CLOCK_MONOTONIC, &stop);
785 94a3f4e9 2024-03-30 thomas timespecsub(&stop, &start, &elapsed);
786 94a3f4e9 2024-03-30 thomas timespecsub(&timeout, &elapsed, &timeout);
787 94a3f4e9 2024-03-30 thomas if (ret == 0 || timeout.tv_sec <= 0)
788 94a3f4e9 2024-03-30 thomas errx(1, "timeout");
789 94a3f4e9 2024-03-30 thomas
790 94a3f4e9 2024-03-30 thomas if (bio.wbuf.len > 0 && (pfd.revents & POLLOUT)) {
791 94a3f4e9 2024-03-30 thomas if (bufio_write(&bio) == -1 && errno != EAGAIN)
792 94a3f4e9 2024-03-30 thomas errx(1, "bufio_write: %s", bufio_io_err(&bio));
793 94a3f4e9 2024-03-30 thomas }
794 94a3f4e9 2024-03-30 thomas if (pfd.revents & POLLIN) {
795 94a3f4e9 2024-03-30 thomas r = bufio_read(&bio);
796 94a3f4e9 2024-03-30 thomas if (r == -1 && errno != EAGAIN)
797 94a3f4e9 2024-03-30 thomas errx(1, "bufio_read: %s", bufio_io_err(&bio));
798 94a3f4e9 2024-03-30 thomas if (r == 0)
799 94a3f4e9 2024-03-30 thomas errx(1, "unexpected EOF");
800 94a3f4e9 2024-03-30 thomas
801 94a3f4e9 2024-03-30 thomas for (;;) {
802 94a3f4e9 2024-03-30 thomas line = buf_getdelim(&bio.rbuf, "\r\n", &len);
803 94a3f4e9 2024-03-30 thomas if (line == NULL)
804 94a3f4e9 2024-03-30 thomas break;
805 94a3f4e9 2024-03-30 thomas if (response_code && *line == '\0') {
806 94a3f4e9 2024-03-30 thomas /*
807 94a3f4e9 2024-03-30 thomas * end of headers, don't bother
808 94a3f4e9 2024-03-30 thomas * reading the body, if there is.
809 94a3f4e9 2024-03-30 thomas */
810 94a3f4e9 2024-03-30 thomas done = 1;
811 94a3f4e9 2024-03-30 thomas break;
812 94a3f4e9 2024-03-30 thomas }
813 94a3f4e9 2024-03-30 thomas if (response_code) {
814 94a3f4e9 2024-03-30 thomas buf_drain(&bio.rbuf, len);
815 94a3f4e9 2024-03-30 thomas continue;
816 94a3f4e9 2024-03-30 thomas }
817 94a3f4e9 2024-03-30 thomas spc = strchr(line, ' ');
818 94a3f4e9 2024-03-30 thomas if (spc == NULL)
819 94a3f4e9 2024-03-30 thomas errx(1, "bad reply");
820 94a3f4e9 2024-03-30 thomas *spc++ = '\0';
821 94a3f4e9 2024-03-30 thomas if (strcasecmp(line, "HTTP/1.1") != 0)
822 94a3f4e9 2024-03-30 thomas errx(1, "unexpected protocol: %s",
823 94a3f4e9 2024-03-30 thomas line);
824 94a3f4e9 2024-03-30 thomas line = spc;
825 94a3f4e9 2024-03-30 thomas
826 94a3f4e9 2024-03-30 thomas spc = strchr(line, ' ');
827 94a3f4e9 2024-03-30 thomas if (spc == NULL)
828 94a3f4e9 2024-03-30 thomas errx(1, "bad reply");
829 94a3f4e9 2024-03-30 thomas *spc++ = '\0';
830 94a3f4e9 2024-03-30 thomas
831 94a3f4e9 2024-03-30 thomas response_code = strtonum(line, 100, 599,
832 94a3f4e9 2024-03-30 thomas &errstr);
833 94a3f4e9 2024-03-30 thomas if (errstr != NULL)
834 94a3f4e9 2024-03-30 thomas errx(1, "response code is %s: %s",
835 94a3f4e9 2024-03-30 thomas errstr, line);
836 94a3f4e9 2024-03-30 thomas
837 94a3f4e9 2024-03-30 thomas buf_drain(&bio.rbuf, len);
838 94a3f4e9 2024-03-30 thomas }
839 94a3f4e9 2024-03-30 thomas if (done)
840 94a3f4e9 2024-03-30 thomas break;
841 94a3f4e9 2024-03-30 thomas }
842 94a3f4e9 2024-03-30 thomas
843 94a3f4e9 2024-03-30 thomas if (!feof(tmpfp) && bio.wbuf.len < sizeof(buf)) {
844 94a3f4e9 2024-03-30 thomas len = fread(buf, 1, sizeof(buf), tmpfp);
845 94a3f4e9 2024-03-30 thomas if (len == 0) {
846 94a3f4e9 2024-03-30 thomas if (ferror(tmpfp))
847 94a3f4e9 2024-03-30 thomas err(1, "fread");
848 94a3f4e9 2024-03-30 thomas continue;
849 94a3f4e9 2024-03-30 thomas }
850 94a3f4e9 2024-03-30 thomas
851 94a3f4e9 2024-03-30 thomas if (bufio_compose(&bio, buf, len) == -1)
852 94a3f4e9 2024-03-30 thomas err(1, "buf_compose");
853 94a3f4e9 2024-03-30 thomas }
854 94a3f4e9 2024-03-30 thomas }
855 94a3f4e9 2024-03-30 thomas
856 37038afa 2024-03-30 thomas if (response_code >= 200 && response_code < 300)
857 94a3f4e9 2024-03-30 thomas return 0;
858 94a3f4e9 2024-03-30 thomas errx(1, "request failed with code %d", response_code);
859 94a3f4e9 2024-03-30 thomas }