ksgi

Форк
0
/
kcgi.c 
1456 строк · 31.7 Кб
1
/*	$Id$ */
2
/*
3
 * Copyright (c) 2012, 2014--2017 Kristaps Dzonsons <kristaps@bsd.lv>
4
 *
5
 * Permission to use, copy, modify, and distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17
#include "config.h"
18

19
#include <sys/mman.h>
20
#include <sys/stat.h>
21
#include <sys/socket.h>
22

23
#include <assert.h>
24
#include <ctype.h>
25
#include <errno.h>
26
#include <fcntl.h>
27
#include <inttypes.h>
28
#include <limits.h>
29
#include <math.h> /* HUGE_VAL */
30
#include <signal.h>
31
#include <stdarg.h>
32
#include <stdint.h>
33
#include <stdio.h>
34
#include <stdlib.h>
35
#include <string.h>
36
#include <unistd.h>
37

38
#include "kcgi.h"
39
#include "extern.h"
40

41
/*
42
 * Maximum size of printing a signed 64-bit integer.
43
 */
44
#define	INT_MAXSZ	 22
45

46
const char *const kschemes[KSCHEME__MAX] = {
47
	"aaa", /* KSCHEME_AAA */
48
	"aaas", /* KSCHEME_AAAS */
49
	"about", /* KSCHEME_ABOUT */
50
	"acap", /* KSCHEME_ACAP */
51
	"acct", /* KSCHEME_ACCT */
52
	"cap", /* KSCHEME_CAP */
53
	"cid", /* KSCHEME_CID */
54
	"coap", /* KSCHEME_COAP */
55
	"coaps", /* KSCHEME_COAPS */
56
	"crid", /* KSCHEME_CRID */
57
	"data", /* KSCHEME_DATA */
58
	"dav", /* KSCHEME_DAV */
59
	"dict", /* KSCHEME_DICT */
60
	"dns", /* KSCHEME_DNS */
61
	"file", /* KSCHEME_FILE */
62
	"ftp", /* KSCHEME_FTP */
63
	"geo", /* KSCHEME_GEO */
64
	"go", /* KSCHEME_GO */
65
	"gopher", /* KSCHEME_GOPHER */
66
	"h323", /* KSCHEME_H323 */
67
	"http", /* KSCHEME_HTTP */
68
	"https", /* KSCHEME_HTTPS */
69
	"iax", /* KSCHEME_IAX */
70
	"icap", /* KSCHEME_ICAP */
71
	"im", /* KSCHEME_IM */
72
	"imap", /* KSCHEME_IMAP */
73
	"info", /* KSCHEME_INFO */
74
	"ipp", /* KSCHEME_IPP */
75
	"iris", /* KSCHEME_IRIS */
76
	"iris.beep", /* KSCHEME_IRIS_BEEP */
77
	"iris.xpc", /* KSCHEME_IRIS_XPC */
78
	"iris.xpcs", /* KSCHEME_IRIS_XPCS */
79
	"iris.lwz", /* KSCHEME_IRIS_LWZ */
80
	"jabber", /* KSCHEME_JABBER */
81
	"ldap", /* KSCHEME_LDAP */
82
	"mailto", /* KSCHEME_MAILTO */
83
	"mid", /* KSCHEME_MID */
84
	"msrp", /* KSCHEME_MSRP */
85
	"msrps", /* KSCHEME_MSRPS */
86
	"mtqp", /* KSCHEME_MTQP */
87
	"mupdate", /* KSCHEME_MUPDATE */
88
	"news", /* KSCHEME_NEWS */
89
	"nfs", /* KSCHEME_NFS */
90
	"ni", /* KSCHEME_NI */
91
	"nih", /* KSCHEME_NIH */
92
	"nntp", /* KSCHEME_NNTP */
93
	"opaquelocktoken", /* KSCHEME_OPAQUELOCKTOKEN */
94
	"pop", /* KSCHEME_POP */
95
	"pres", /* KSCHEME_PRES */
96
	"reload", /* KSCHEME_RELOAD */
97
	"rtsp", /* KSCHEME_RTSP */
98
	"rtsps", /* KSCHEME_RTSPS */
99
	"rtspu", /* KSCHEME_RTSPU */
100
	"service", /* KSCHEME_SERVICE */
101
	"session", /* KSCHEME_SESSION */
102
	"shttp", /* KSCHEME_SHTTP */
103
	"sieve", /* KSCHEME_SIEVE */
104
	"sip", /* KSCHEME_SIP */
105
	"sips", /* KSCHEME_SIPS */
106
	"sms", /* KSCHEME_SMS */
107
	"snmp", /* KSCHEME_SNMP */
108
	"soap.beep", /* KSCHEME_SOAP_BEEP */
109
	"soap.beeps", /* KSCHEME_SOAP_BEEPS */
110
	"stun", /* KSCHEME_STUN */
111
	"stuns", /* KSCHEME_STUNS */
112
	"tag", /* KSCHEME_TAG */
113
	"tel", /* KSCHEME_TEL */
114
	"telnet", /* KSCHEME_TELNET */
115
	"tftp", /* KSCHEME_TFTP */
116
	"thismessage", /* KSCHEME_THISMESSAGE */
117
	"tn3270", /* KSCHEME_TN3270 */
118
	"tip", /* KSCHEME_TIP */
119
	"turn", /* KSCHEME_TURN */
120
	"turns", /* KSCHEME_TURNS */
121
	"tv", /* KSCHEME_TV */
122
	"urn", /* KSCHEME_URN */
123
	"vemmi", /* KSCHEME_VEMMI */
124
	"ws", /* KSCHEME_WS */
125
	"wss", /* KSCHEME_WSS */
126
	"xcon", /* KSCHEME_XCON */
127
	"xcon-userid", /* KSCHEME_XCON_USERID */
128
	"xmlrpc.beep", /* KSCHEME_XMLRPC_BEEP */
129
	"xmlrpc.beeps", /* KSCHEME_XMLRPC_BEEPS */
130
	"xmpp", /* KSCHEME_XMPP */
131
	"z39.50r", /* KSCHEME_Z39_50R */
132
	"z39.50s", /* KSCHEME_Z39_50S */
133
};
134

135
const char *const kresps[KRESP__MAX] = {
136
	"Access-Control-Allow-Origin", /* KRESP_ACCESS_CONTROL_ALLOW_ORIGIN */
137
	"Accept-Ranges", /* KRESP_ACCEPT_RANGES */
138
	"Age", /* KRESP_AGE */
139
	"Allow", /* KRESP_ALLOW */
140
	"Cache-Control", /* KRESP_CACHE_CONTROL */
141
	"Connection", /* KRESP_CONNECTION */
142
	"Content-Encoding", /* KRESP_CONTENT_ENCODING */
143
	"Content-Language", /* KRESP_CONTENT_LANGUAGE */
144
	"Content-Length", /* KRESP_CONTENT_LENGTH */
145
	"Content-Location", /* KRESP_CONTENT_LOCATION */
146
	"Content-MD5", /* KRESP_CONTENT_MD5 */
147
	"Content-Disposition", /* KRESP_CONTENT_DISPOSITION */
148
	"Content-Range", /* KRESP_CONTENT_RANGE */
149
	"Content-Type", /* KRESP_CONTENT_TYPE */
150
	"Date", /* KRESP_DATE */
151
	"ETag", /* KRESP_ETAG */
152
	"Expires", /* KRESP_EXPIRES */
153
	"Last-Modified", /* KRESP_LAST_MODIFIED */
154
	"Link", /* KRESP_LINK */
155
	"Location", /* KRESP_LOCATION */
156
	"P3P", /* KRESP_P3P */
157
	"Pragma", /* KRESP_PRAGMA */
158
	"Proxy-Authenticate", /* KRESP_PROXY_AUTHENTICATE */
159
	"Refresh", /* KRESP_REFRESH */
160
	"Retry-After", /* KRESP_RETRY_AFTER */
161
	"Server", /* KRESP_SERVER */
162
	"Set-Cookie", /* KRESP_SET_COOKIE */
163
	"Status", /* KRESP_STATUS */
164
	"Strict-Transport-Security", /* KRESP_STRICT_TRANSPORT_SECURITY */
165
	"Trailer", /* KRESP_TRAILER */
166
	"Transfer-Encoding", /* KRESP_TRANSFER_ENCODING */
167
	"Upgrade", /* KRESP_UPGRADE */
168
	"Vary", /* KRESP_VARY */
169
	"Via", /* KRESP_VIA */
170
	"Warning", /* KRESP_WARNING */
171
	"WWW-Authenticate", /* KRESP_WWW_AUTHENTICATE */
172
	"X-Frame-Options", /* KRESP_X_FRAME_OPTIONS */
173
};
174

175
const char *const kmimetypes[KMIME__MAX] = {
176
	"application/x-javascript", /* KMIME_APP_JAVASCRIPT */
177
	"application/json", /* KMIME_APP_JSON */
178
	"application/octet-stream", /* KMIME_APP_OCTET_STREAM */
179
	"application/pdf", /* KMIME_APP_PDF */
180
	"application/xml", /* KMIME_APP_XML */
181
	"application/zip", /* KMIME_APP_ZIP */
182
	"image/gif", /* KMIME_IMAGE_GIF */
183
	"image/jpeg", /* KMIME_IMAGE_JPEG */
184
	"image/png", /* KMIME_IMAGE_PNG */
185
	"image/svg+xml", /* KMIME_IMAGE_SVG_XML */
186
	"text/calendar", /* KMIME_TEXT_CALENDAR */
187
	"text/css", /* KMIME_TEXT_CSS */
188
	"text/csv", /* KMIME_TEXT_CSV */
189
	"text/html", /* KMIME_TEXT_HTML */
190
	"text/plain", /* KMIME_TEXT_PLAIN */
191
	"text/xml", /* KMIME_TEXT_XML */
192
};
193

194
const char *const khttps[KHTTP__MAX] = {
195
	"100 Continue",				/* KHTTP_100 */
196
	"101 Switching Protocols",              /* KHTTP_101 */
197
	"103 Checkpoint",                       /* KHTTP_103 */
198
	"200 OK",                               /* KHTTP_200 */
199
	"201 Created",                          /* KHTTP_201 */
200
	"202 Accepted",                         /* KHTTP_202 */
201
	"203 Non-Authoritative Information",    /* KHTTP_203 */
202
	"204 No Content",                       /* KHTTP_204 */
203
	"205 Reset Content",                    /* KHTTP_205 */
204
	"206 Partial Content",                  /* KHTTP_206 */
205
	"207 Multi-Status",                     /* KHTTP_207 */
206
	"300 Multiple Choices",                 /* KHTTP_300 */
207
	"301 Moved Permanently",                /* KHTTP_301 */
208
	"302 Found",                            /* KHTTP_302 */
209
	"303 See Other",                        /* KHTTP_303 */
210
	"304 Not Modified",                     /* KHTTP_304 */
211
	"306 Switch Proxy",                     /* KHTTP_306 */
212
	"307 Temporary Redirect",               /* KHTTP_307 */
213
	"308 Resume Incomplete",                /* KHTTP_308 */
214
	"400 Bad Request",                      /* KHTTP_400 */
215
	"401 Unauthorized",                     /* KHTTP_401 */
216
	"402 Payment Required",                 /* KHTTP_402 */
217
	"403 Forbidden",                        /* KHTTP_403 */
218
	"404 Not Found",                        /* KHTTP_404 */
219
	"405 Method Not Allowed",               /* KHTTP_405 */
220
	"406 Not Acceptable",                   /* KHTTP_406 */
221
	"407 Proxy Authentication Required",    /* KHTTP_407 */
222
	"408 Request Timeout",                  /* KHTTP_408 */
223
	"409 Conflict",                         /* KHTTP_409 */
224
	"410 Gone",                             /* KHTTP_410 */
225
	"411 Length Required",                  /* KHTTP_411 */
226
	"412 Precondition Failed",              /* KHTTP_412 */
227
	"413 Request Entity Too Large",         /* KHTTP_413 */
228
	"414 Request-URI Too Long",             /* KHTTP_414 */
229
	"415 Unsupported Media Type",           /* KHTTP_415 */
230
	"416 Requested Range Not Satisfiable",  /* KHTTP_416 */
231
	"417 Expectation Failed",               /* KHTTP_417 */
232
	"424 Failed Dependency",                /* KHTTP_424 */
233
	"428 Precondition Required",            /* KHTTP_428 */
234
	"429 Too Many Requests",                /* KHTTP_429 */
235
	"431 Request Header Fields Too Large",  /* KHTTP_431 */
236
	"500 Internal Server Error",            /* KHTTP_500 */
237
	"501 Not Implemented",                  /* KHTTP_501 */
238
	"502 Bad Gateway",                      /* KHTTP_502 */
239
	"503 Service Unavailable",              /* KHTTP_503 */
240
	"504 Gateway Timeout",                  /* KHTTP_504 */
241
	"505 HTTP Version Not Supported",       /* KHTTP_505 */
242
	"507 Insufficient Storage",             /* KHTTP_507 */
243
	"511 Network Authentication Required",  /* KHTTP_511 */
244
};
245

246
/*
247
 * This doesn't have a preset size.
248
 */
249
const struct kmimemap ksuffixmap[] = {
250
	{ "css", KMIME_TEXT_CSS },
251
	{ "csv", KMIME_TEXT_CSV },
252
	{ "gif", KMIME_IMAGE_GIF },
253
	{ "htm", KMIME_TEXT_HTML },
254
	{ "html", KMIME_TEXT_HTML },
255
	{ "ical", KMIME_TEXT_CALENDAR },
256
	{ "icalendar", KMIME_TEXT_CALENDAR },
257
	{ "ics", KMIME_TEXT_CALENDAR },
258
	{ "ifb", KMIME_TEXT_CALENDAR },
259
	{ "jpg", KMIME_IMAGE_JPEG },
260
	{ "jpeg", KMIME_IMAGE_JPEG },
261
	{ "js", KMIME_APP_JAVASCRIPT },
262
	{ "json", KMIME_APP_JSON },
263
	{ "pdf", KMIME_APP_PDF },
264
	{ "png", KMIME_IMAGE_PNG },
265
	{ "shtml", KMIME_TEXT_HTML },
266
	{ "svg", KMIME_IMAGE_SVG_XML },
267
	{ "svgz", KMIME_IMAGE_SVG_XML },
268
	{ "txt", KMIME_TEXT_PLAIN },
269
	{ "xml", KMIME_TEXT_XML },
270
	{ "zip", KMIME_APP_ZIP },
271
	{ NULL, KMIME__MAX },
272
};
273

274
/*
275
 * Default MIME suffix per type.
276
 */
277
const char *const ksuffixes[KMIME__MAX] = {
278
	"js",	/* KMIME_APP_JAVASCRIPT */
279
	"json", /* KMIME_APP_JSON */
280
	NULL,	/* KMIME_APP_OCTET_STREAM */
281
	"pdf",	/* KMIME_APP_PDF */
282
	"xml",	/* KMIME_APP_XML */
283
	"zip",	/* KMIME_APP_ZIP */
284
	"gif",	/* KMIME_IMAGE_GIF */
285
	"jpg",	/* KMIME_IMAGE_JPEG */
286
	"png",	/* KMIME_IMAGE_PNG */
287
	"svg",	/* KMIME_IMAGE_PNG */
288
	"ics",	/* KMIME_TEXT_CALENDAR */
289
	"css",	/* KMIME_TEXT_CSS */
290
	"csv",	/* KMIME_TEXT_CSV */
291
	"html",	/* KMIME_TEXT_HTML */
292
	"txt",	/* KMIME_TEXT_PLAIN */
293
	"xml",	/* KMIME_TEXT_XML */
294
};
295

296
const char *const kerrors[] = {
297
	"success", 			/* KCGI_OK */
298
	"cannot allocate memory",	/* KCGI_ENOMEM */
299
	"FastCGI exit",			/* KCGI_EXIT */
300
	"end-point connection closed",	/* KCGI_HUP */
301
	"too many open sockets",	/* KCGI_ENFILE */
302
	"failed to fork child",		/* KCGI_EAGAIN */
303
	"internal error",		/* KCGI_FORM */
304
	"system error",			/* KCGI_SYSTEM */
305
	"writer error",			/* KCGI_WRITER */
306
};
307

308
const char *
309
kcgi_strerror(enum kcgi_err er)
310
{
311

312
	assert(er <= KCGI_WRITER);
313
	return kerrors[er];
314
}
315

316
/*
317
 * Safe strdup(): don't return on exhaustion.
318
 */
319
char *
320
kstrdup(const char *cp)
321
{
322
	char	*p;
323

324
	if ((p = kxstrdup(cp)) == NULL)
325
		exit(EXIT_FAILURE);
326
	return p;
327
}
328

329
/*
330
 * Safe realloc(): don't return on exhaustion.
331
 */
332
void *
333
krealloc(void *pp, size_t sz)
334
{
335
	char	*p;
336

337
	if ((p = kxrealloc(pp, sz)) == NULL)
338
		exit(EXIT_FAILURE);
339
	return p;
340
}
341

342
/*
343
 * Safe reallocarray(): don't return on exhaustion.
344
 */
345
void *
346
kreallocarray(void *pp, size_t nm, size_t sz)
347
{
348
	char	*p;
349

350
	if ((p = kxreallocarray(pp, nm, sz)) == NULL)
351
		exit(EXIT_FAILURE);
352
	return p;
353
}
354

355
/*
356
 * Safe asprintf(): don't return on exhaustion.
357
 */
358
int
359
kasprintf(char **p, const char *fmt, ...)
360
{
361
	va_list	 ap;
362
	int	 len;
363

364
	va_start(ap, fmt);
365
	len = kxvasprintf(p, fmt, ap);
366
	va_end(ap);
367
	if (len == -1)
368
		exit(EXIT_FAILURE);
369
	return len;
370
}
371

372
/*
373
 * Safe vasprintf(): don't return on exhaustion.
374
 */
375
int
376
kvasprintf(char **p, const char *fmt, va_list ap)
377
{
378
	int	 len;
379

380
	if ((len = kxvasprintf(p, fmt, ap)) == -1)
381
		exit(EXIT_FAILURE);
382
	return len;
383
}
384

385
/*
386
 * Safe calloc(): don't return on exhaustion.
387
 */
388
void *
389
kcalloc(size_t nm, size_t sz)
390
{
391
	char	*p;
392

393
	if ((p = kxcalloc(nm, sz)) == NULL)
394
		exit(EXIT_FAILURE);
395
	return p;
396
}
397

398
/*
399
 * Safe malloc(): don't return on exhaustion.
400
 */
401
void *
402
kmalloc(size_t sz)
403
{
404
	char	*p;
405

406
	if ((p = kxmalloc(sz)) == NULL)
407
		exit(EXIT_FAILURE);
408
	return p;
409
}
410

411
/*
412
 * Deprecated form.
413
 */
414
char *
415
kutil_urlencode(const char *cp)
416
{
417

418
	return (cp == NULL) ? NULL : khttp_urlencode(cp);
419
}
420

421
char *
422
khttp_urlencode(const char *cp)
423
{
424
	char	*p;
425
	char	 ch;
426
	size_t	 sz, cur;
427
	int	 rc;
428

429
	if (cp == NULL)
430
		return kxstrdup("");
431

432
	/* 
433
	 * Leave three bytes per input byte for encoding. 
434
	 * This ensures we needn't range-check.
435
	 * First check whether our size overflows. 
436
	 * We do this here because we need our size!
437
	 */
438

439
	sz = strlen(cp) + 1;
440
	if (SIZE_MAX / 3 < sz) {
441
		kutil_warnx(NULL, NULL, "multiplicative overflow");
442
		return NULL;
443
	}
444
	if ((p = kxcalloc(sz, 3)) == NULL)
445
		return NULL;
446

447
	for (cur = 0; (ch = *cp) != '\0'; cp++) {
448
		if (isalnum((unsigned char)ch) || ch == '-' || 
449
		    ch == '_' || ch == '.' || ch == '~') {
450
			p[cur++] = ch;
451
			continue;
452
		} else if (' ' == ch) {
453
			p[cur++] = '+';
454
			continue;
455
		}
456
		rc = snprintf(p + cur, 4, "%%%.2hhX", 
457
			(unsigned char)ch);
458
		if (rc != 3) {
459
			kutil_warnx(NULL, NULL, "snprintf");
460
			free(p);
461
			return NULL;
462
		}
463
		cur += 3;
464
	}
465

466
	return p;
467
}
468

469
/*
470
 * Deprecated form.
471
 */
472
enum kcgi_err
473
kutil_urldecode_inplace(char *p)
474
{
475

476
	return khttp_urldecode_inplace(p);
477
}
478

479
enum kcgi_err
480
khttp_urldecode_inplace(char *p)
481
{
482
	char	 	 c, d;
483
	const char	*tail;
484

485
	if (p == NULL)
486
		return KCGI_FORM;
487

488
	/*
489
	 * Keep track of two positions: "p", where we'll write the
490
	 * decoded results, and "tail", which is from where we'll
491
	 * decode hex or copy data.
492
	 */
493

494
	for (tail = p; (c = *tail) != '\0'; *p++ = c) {
495
		if (c != '%') {
496
			if (c == '+')
497
				c = ' ';
498
			tail++;
499
			continue;
500
		}
501

502
		/* 
503
		 * Read hex '%xy' as two unsigned chars "c" and "d" then
504
		 * combine them back into "c".
505
		 */
506

507
		if (sscanf(tail + 1, "%1hhx%1hhx", &d, &c) != 2 ||
508
		    (c |= d << 4) == '\0') {
509
			kutil_warnx(NULL, NULL, 
510
				"malformed percent-encoded sequence");
511
			return KCGI_FORM;
512
		}
513
		tail += 3;
514
	}
515

516
	*p = '\0';
517
	return KCGI_OK;
518
}
519

520
/*
521
 * Deprecated form.
522
 */
523
enum kcgi_err
524
kutil_urldecode(const char *src, char **dst)
525
{
526

527
	return khttp_urldecode(src, dst);
528
}
529

530
enum kcgi_err
531
khttp_urldecode(const char *src, char **dst)
532
{
533
	enum kcgi_err	 er;
534

535
	/* In case of error. */
536

537
	if (dst != NULL)
538
		*dst = NULL;
539

540
	if (src == NULL || dst == NULL)
541
		return KCGI_FORM;
542
	if ((*dst = kxstrdup(src)) == NULL)
543
		return KCGI_ENOMEM;
544
	if ((er = khttp_urldecode_inplace(*dst)) == KCGI_OK)
545
		return KCGI_OK;
546

547
	/* If we have decoding errors, clear the output. */
548

549
	free(*dst);
550
	*dst = NULL;
551
	return er;
552
}
553

554
/*
555
 * Append key-value triplets in "ap" to "p".
556
 * Reallocate as necessary.
557
 * A NULL key signifies termination of the list.
558
 * Returns the URL string or NULL on memory failure.
559
 */
560
static char *
561
khttp_url_query_string(char *p, va_list ap)
562
{
563
	char	*pp, *keyp, *valp;
564
	size_t	 total, count = 0;
565

566
	total = strlen(p) + 1;
567

568
	while ((pp = va_arg(ap, char *)) != NULL) {
569
		if ((keyp = khttp_urlencode(pp)) == NULL) {
570
			free(p);
571
			return NULL;
572
		}
573

574
		valp = khttp_urlencode(va_arg(ap, char *));
575
		if (valp == NULL) {
576
			free(p);
577
			free(keyp);
578
			return NULL;
579
		}
580

581
		/* Size for key, value, ? or &, and =. */
582
		/* FIXME: check for overflow! */
583

584
		total += strlen(keyp) + strlen(valp) + 2;
585

586
		if ((pp = kxrealloc(p, total)) == NULL) {
587
			free(p);
588
			free(keyp);
589
			free(valp);
590
			return NULL;
591
		}
592
		p = pp;
593

594
		if (count > 0)
595
			(void)strlcat(p, "&", total);
596
		else
597
			(void)strlcat(p, "?", total);
598

599
		(void)strlcat(p, keyp, total);
600
		(void)strlcat(p, "=", total);
601
		(void)strlcat(p, valp, total);
602

603
		free(keyp);
604
		free(valp);
605
		count++;
606
	}
607

608
	return p;
609
}
610

611
/*
612
 * Append key-type-value triplets in "ap" to "p".
613
 * Reallocate as necessary.
614
 * A NULL key signifies termination of the list.
615
 * Returns the URL string or NULL on memory failure.
616
 */
617
static char *
618
khttp_url_query_stringx(char *p, va_list ap)
619
{
620
	char	*pp, *keyp, *valp, *valpp;
621
	size_t	 total, count = 0;
622
	char 	 buf[256]; /* max double/int64_t */
623

624
	total = strlen(p) + 1;
625

626
	while ((pp = va_arg(ap, char *)) != NULL) {
627
		if ((keyp = khttp_urlencode(pp)) == NULL) {
628
			free(p);
629
			return NULL;
630
		}
631

632
		valp = valpp = NULL;
633

634
		switch (va_arg(ap, enum kattrx)) {
635
		case KATTRX_STRING:
636
			valp = khttp_urlencode(va_arg(ap, char *));
637
			valpp = valp;
638
			break;
639
		case KATTRX_INT:
640
			(void)snprintf(buf, sizeof(buf),
641
				"%" PRId64, va_arg(ap, int64_t));
642
			valp = buf;
643
			break;
644
		case KATTRX_DOUBLE:
645
			(void)snprintf(buf, sizeof(buf),
646
				"%g", va_arg(ap, double));
647
			valp = buf;
648
			break;
649
		default:
650
			free(p);
651
			free(keyp);
652
			return NULL;
653
		}
654

655
		if (valp == NULL) {
656
			free(p);
657
			free(keyp);
658
			return NULL;
659
		}
660

661
		/* Size for key, value, ? or &, and =. */
662
		/* FIXME: check for overflow! */
663

664
		total += strlen(keyp) + strlen(valp) + 2;
665

666
		if ((pp = kxrealloc(p, total)) == NULL) {
667
			free(p);
668
			free(keyp);
669
			free(valpp);
670
			return NULL;
671
		}
672
		p = pp;
673

674
		if (count > 0)
675
			(void)strlcat(p, "&", total);
676
		else
677
			(void)strlcat(p, "?", total);
678

679
		(void)strlcat(p, keyp, total);
680
		(void)strlcat(p, "=", total);
681
		(void)strlcat(p, valp, total);
682

683
		free(keyp);
684
		free(valpp);
685
		count++;
686
	}
687

688
	return p;
689
}
690

691
/*
692
 * Deprecated form.
693
 */
694
char *
695
kutil_urlabs(enum kscheme scheme, 
696
	const char *host, uint16_t port, const char *path)
697
{
698
	char	*p;
699

700
	kxasprintf(&p, "%s://%s:%" PRIu16 "%s", 
701
	    kschemes[scheme], host, port, path);
702
	return p;
703
}
704

705
char *
706
khttp_urlabs(enum kscheme scheme, const char *host,
707
	uint16_t port, const char *path, ...)
708
{
709
	char	*p;
710
	va_list	 ap;
711

712
	va_start(ap, path);
713
	p = khttp_vurlabs(scheme, host, port, path, ap);
714
	va_end(ap);
715
	return p;
716
}
717

718
char *
719
khttp_vurlabs(enum kscheme scheme, const char *host,
720
	uint16_t port, const char *path, va_list ap)
721
{
722
	char	*p;
723
	int	 len;
724

725
	if (host == NULL || host[0] == '\0') {
726
		len = kxasprintf(&p, "%s:%s", kschemes[scheme], 
727
			path == NULL ? "" : path);
728
	} else if (port == 0) {
729
		len = kxasprintf(&p, "%s://%s%s%s", 
730
		    kschemes[scheme], host, 
731
		    path != NULL && path[0] != '\0' && 
732
		    	path[0] != '/' ? "/" : "", 
733
		    path == NULL ? "" : path);
734
	} else {
735
		len = kxasprintf(&p, "%s://%s:%" PRIu16 "%s%s", 
736
		    kschemes[scheme], host, port, 
737
		    path != NULL && path[0] != '\0' && 
738
		    	path[0] != '/' ? "/" : "", 
739
		    path == NULL ? "" : path);
740
	}
741

742
	if (len == -1)
743
		return NULL;
744
	return khttp_url_query_string(p, ap);
745
}
746

747
/*
748
 * Deprecated form.
749
 */
750
char *
751
kutil_urlpartx(struct kreq *req, const char *path,
752
	const char *mime, const char *page, ...)
753
{
754
	char	*ret;
755
	va_list	 ap;
756

757
	if (page == NULL)
758
		return NULL;
759

760
	va_start(ap, page);
761
	ret = khttp_vurlpartx(path, mime, page, ap);
762
	va_end(ap);
763
	return ret;
764
}
765

766
char *
767
khttp_urlpartx(const char *path,
768
	const char *mime, const char *page, ...)
769
{
770
	char	*ret;
771
	va_list	 ap;
772

773
	va_start(ap, page);
774
	ret = khttp_vurlpartx(path, mime, page, ap);
775
	va_end(ap);
776
	return ret;
777
}
778

779
char *
780
khttp_vurlpartx(const char *path,
781
	const char *mime, const char *page, va_list ap)
782
{
783
	int	 len;
784
	char	*p, *pageenc = NULL;
785

786
	if (page != NULL && (pageenc = khttp_urlencode(page)) == NULL)
787
		return NULL;
788

789
	if ((mime == NULL || mime[0] == '\0') || 
790
	    (page == NULL || page[0] == '\0'))
791
		len = kxasprintf(&p, "%s%s%s", 
792
			path != NULL ? path : "",
793
			path != NULL ? "/" : "", 
794
			pageenc != NULL ? pageenc : "");
795
	else {
796
		assert(pageenc != NULL);
797
		len = kxasprintf(&p, "%s%s%s.%s", 
798
			path != NULL ? path : "",
799
			path != NULL ? "/" : "", pageenc, mime);
800
	}
801

802
	free(pageenc);
803
	pageenc = NULL;
804

805
	if (len == -1)
806
		return NULL;
807
	return khttp_url_query_stringx(p, ap);
808
}
809

810
/*
811
 * Deprecated form.
812
 */
813
char *
814
kutil_urlpart(struct kreq *req, const char *path,
815
	const char *mime, const char *page, ...)
816
{
817
	char	*ret;
818
	va_list	 ap;
819

820
	if (page == NULL)
821
		return NULL;
822
	va_start(ap, page);
823
	ret = khttp_vurlpart(path, mime, page, ap);
824
	va_end(ap);
825
	return ret;
826
}
827

828
char *
829
khttp_urlpart(const char *path,
830
	const char *mime, const char *page, ...)
831
{
832
	char	*ret;
833
	va_list	 ap;
834

835
	va_start(ap, page);
836
	ret = khttp_vurlpart(path, mime, page, ap);
837
	va_end(ap);
838
	return ret;
839
}
840

841
char *
842
khttp_vurlpart(const char *path,
843
	const char *mime, const char *page, va_list ap)
844
{
845
	char	*p, *pageenc = NULL;
846
	int	 len;
847

848
	if (page != NULL && (pageenc = khttp_urlencode(page)) == NULL)
849
		return NULL;
850

851
	/* 
852
	 * Only append the MIME suffix if we have it AND if the page is
853
	 * non-NULL and non-empty.
854
	 */
855

856
	if ((mime == NULL || mime[0] == '\0') || 
857
	    (page == NULL || page[0] == '\0'))
858
		len = kxasprintf(&p, "%s%s%s", 
859
			path != NULL ? path : "",
860
			path != NULL ? "/" : "", 
861
			pageenc != NULL ? pageenc : "");
862
	else {
863
		assert(pageenc != NULL);
864
		len = kxasprintf(&p, "%s%s%s.%s", 
865
			path != NULL ? path : "",
866
			path != NULL ? "/" : "", pageenc, mime);
867
	}
868

869
	free(pageenc);
870
	pageenc = NULL;
871

872
	if (len == -1)
873
		return NULL;
874
	return khttp_url_query_string(p, ap);
875
}
876

877
static void
878
kpair_free(struct kpair *p, size_t sz)
879
{
880
	size_t	 i;
881

882
	for (i = 0; i < sz; i++) {
883
		free(p[i].key);
884
		free(p[i].val);
885
		free(p[i].file);
886
		free(p[i].ctype);
887
		free(p[i].xcode);
888
	}
889
	free(p);
890
}
891

892
void
893
kreq_free(struct kreq *req)
894
{
895
	size_t	 i;
896

897
	for (i = 0; i < req->reqsz; i++) {
898
		free(req->reqs[i].key);
899
		free(req->reqs[i].val);
900
	}
901

902
	free(req->reqs);
903
	kpair_free(req->cookies, req->cookiesz);
904
	kpair_free(req->fields, req->fieldsz);
905
	free(req->path);
906
	free(req->fullpath);
907
	free(req->remote);
908
	free(req->host);
909
	free(req->cookiemap);
910
	free(req->cookienmap);
911
	free(req->fieldmap);
912
	free(req->fieldnmap);
913
	free(req->suffix);
914
	free(req->pagename);
915
	free(req->pname);
916
	free(req->rawauth.digest);
917

918
	if (req->rawauth.type == KAUTH_DIGEST) {
919
		free(req->rawauth.d.digest.user);
920
		free(req->rawauth.d.digest.uri);
921
		free(req->rawauth.d.digest.realm);
922
		free(req->rawauth.d.digest.nonce);
923
		free(req->rawauth.d.digest.cnonce);
924
		free(req->rawauth.d.digest.response);
925
		free(req->rawauth.d.digest.opaque);
926
	} else if (req->rawauth.type == KAUTH_BASIC) {
927
		free(req->rawauth.d.basic.response);
928
	} else if (req->rawauth.type == KAUTH_BEARER) {
929
		free(req->rawauth.d.basic.response);
930
	}
931
}
932

933
enum kcgi_err
934
khttp_parse(struct kreq *req, 
935
	const struct kvalid *keys, size_t keysz,
936
	const char *const *pages, size_t pagesz,
937
	size_t defpage)
938
{
939

940
	return khttp_parsex(req, ksuffixmap, kmimetypes, 
941
		KMIME__MAX, keys, keysz, pages, pagesz, 
942
		KMIME_TEXT_HTML, defpage, NULL, NULL, 0, NULL);
943
}
944

945
enum kcgi_err
946
khttp_parsex(struct kreq *req, 
947
	const struct kmimemap *suffixmap, 
948
	const char *const *mimes, size_t mimesz,
949
	const struct kvalid *keys, size_t keysz,
950
	const char *const *pages, size_t pagesz,
951
	size_t defmime, size_t defpage, void *arg,
952
	void (*argfree)(void *arg), unsigned debugging,
953
	const struct kopts *opts)
954
{
955
	const struct kmimemap *mm;
956
	enum kcgi_err	  kerr;
957
	int 		  er;
958
	struct kopts	  kopts;
959
	int		  work_dat[2];
960
	pid_t		  work_pid;
961

962
	memset(req, 0, sizeof(struct kreq));
963

964
	/*
965
	 * We'll be using poll(2) for reading our HTTP document, so this
966
	 * must be non-blocking in order to make the reads not spin the
967
	 * CPU.
968
	 */
969

970
	if (kxsocketprep(STDIN_FILENO) != KCGI_OK)
971
		return KCGI_SYSTEM;
972
	if (kxsocketpair(work_dat) != KCGI_OK)
973
		return KCGI_SYSTEM;
974

975
	if ((work_pid = fork()) == -1) {
976
		er = errno;
977
		kutil_warn(NULL, NULL, "fork");
978

979
		close(work_dat[KWORKER_PARENT]);
980
		close(work_dat[KWORKER_CHILD]);
981
		return (er == EAGAIN) ? KCGI_EAGAIN : KCGI_ENOMEM;
982
	} else if (work_pid == 0) {
983
		if (argfree != NULL)
984
			(*argfree)(arg);
985

986
		close(STDOUT_FILENO);
987
		close(work_dat[KWORKER_PARENT]);
988

989
		er = EXIT_SUCCESS;
990
		if (!ksandbox_init_child(SAND_WORKER,
991
		    work_dat[KWORKER_CHILD], -1, -1, -1))
992
			er = EXIT_FAILURE;
993
		else if (kworker_child(work_dat[KWORKER_CHILD],
994
		    keys, keysz, mimes, mimesz, debugging) != KCGI_OK)
995
			er = EXIT_FAILURE;
996

997
		close(work_dat[KWORKER_CHILD]);
998
		_exit(er);
999
		/* NOTREACHED */
1000
	}
1001

1002
	close(work_dat[KWORKER_CHILD]);
1003
	work_dat[KWORKER_CHILD] = -1;
1004

1005
	if (opts == NULL) {
1006
		memset(&kopts, 0, sizeof(struct kopts));
1007
		kopts.sndbufsz = -1;
1008
	} else
1009
		kopts = *opts;
1010

1011
	if (kopts.sndbufsz < 0)
1012
		kopts.sndbufsz = 1024 * 8;
1013

1014
	kerr = KCGI_ENOMEM;
1015

1016
	/*
1017
	 * After this point, all errors should use "goto err", which
1018
	 * will properly free up our memory and close any extant file
1019
	 * descriptors.
1020
	 * Also, we're running our child in the background, so make sure
1021
	 * that it gets killed!
1022
	 */
1023

1024
	req->arg = arg;
1025
	req->keys = keys;
1026
	req->keysz = keysz;
1027
	req->kdata = kdata_alloc(-1, -1, 0, debugging, &kopts);
1028
	if (req->kdata == NULL)
1029
		goto err;
1030

1031
	if (keysz) {
1032
		req->cookiemap = kxcalloc(keysz, sizeof(struct kpair *));
1033
		if (req->cookiemap == NULL)
1034
			goto err;
1035
		req->cookienmap = kxcalloc(keysz, sizeof(struct kpair *));
1036
		if (req->cookienmap == NULL)
1037
			goto err;
1038
		req->fieldmap = kxcalloc(keysz, sizeof(struct kpair *));
1039
		if (req->fieldmap == NULL)
1040
			goto err;
1041
		req->fieldnmap = kxcalloc(keysz, sizeof(struct kpair *));
1042
		if (req->fieldnmap == NULL)
1043
			goto err;
1044
	}
1045

1046
	/*
1047
	 * Now read the input fields from the child and conditionally
1048
	 * assign them to our lookup table.
1049
	 */
1050

1051
	kerr = kworker_parent
1052
		(work_dat[KWORKER_PARENT], req, 1, mimesz);
1053
	if (kerr != KCGI_OK)
1054
		goto err;
1055

1056
	/* Look up page type from component. */
1057

1058
	req->page = defpage;
1059
	if (*req->pagename != '\0')
1060
		for (req->page = 0; req->page < pagesz; req->page++)
1061
			if (strcasecmp
1062
			    (pages[req->page], req->pagename) == 0)
1063
				break;
1064

1065
	/*
1066
	 * Look up the MIME type, defaulting to defmime if none.
1067
	 * If we can't find it, use the maximum (mimesz).
1068
	 */
1069

1070
	req->mime = defmime;
1071
	if (*req->suffix != '\0') {
1072
		for (mm = suffixmap; mm->name != NULL; mm++)
1073
			if (strcasecmp(mm->name, req->suffix) == 0) {
1074
				req->mime = mm->mime;
1075
				break;
1076
			}
1077
		if (mm->name == NULL)
1078
			req->mime = mimesz;
1079
	}
1080

1081
	close(work_dat[KWORKER_PARENT]);
1082
	work_dat[KWORKER_PARENT] = -1;
1083
	kerr = kxwaitpid(work_pid);
1084
	work_pid = -1;
1085
	if (kerr != KCGI_OK)
1086
		goto err;
1087
	return kerr;
1088
err:
1089
	assert(kerr != KCGI_OK);
1090
	if (work_dat[KWORKER_PARENT] != -1)
1091
		close(work_dat[KWORKER_PARENT]);
1092
	if (work_pid != -1)
1093
		kxwaitpid(work_pid);
1094
	kdata_free(req->kdata, 0);
1095
	req->kdata = NULL;
1096
	kreq_free(req);
1097
	return kerr;
1098
}
1099

1100
void
1101
kutil_invalidate(struct kreq *r, struct kpair *kp)
1102
{
1103
	struct kpair	*p, *lastp;
1104
	size_t		 i;
1105

1106
	if (kp == NULL)
1107
		return;
1108

1109
	kp->type = KPAIR__MAX;
1110
	kp->state = KPAIR_INVALID;
1111
	memset(&kp->parsed, 0, sizeof(union parsed));
1112

1113
	/* We're not bucketed. */
1114

1115
	if ((i = kp->keypos) == r->keysz)
1116
		return;
1117

1118
	/* Is it in our fieldmap? */
1119

1120
	if (r->fieldmap[i] != NULL) {
1121
		if (kp == r->fieldmap[i]) {
1122
			r->fieldmap[i] = kp->next;
1123
			kp->next = r->fieldnmap[i];
1124
			r->fieldnmap[i] = kp;
1125
			return;
1126
		} 
1127
		lastp = r->fieldmap[i];
1128
		p = lastp->next;
1129
		for ( ; p != NULL; lastp = p, p = p->next)
1130
			if (kp == p) {
1131
				lastp->next = kp->next;
1132
				kp->next = r->fieldnmap[i];
1133
				r->fieldnmap[i] = kp;
1134
				return;
1135
			}
1136
	}
1137

1138
	/* ...cookies? */
1139

1140
	if (r->cookiemap[i] != NULL) {
1141
		if (kp == r->cookiemap[i]) {
1142
			r->cookiemap[i] = kp->next;
1143
			kp->next = r->cookienmap[i];
1144
			r->cookienmap[i] = kp;
1145
			return;
1146
		} 
1147
		lastp = r->cookiemap[i];
1148
		p = lastp->next;
1149
		for ( ; p != NULL; lastp = p, p = p->next) 
1150
			if (kp == p) {
1151
				lastp->next = kp->next;
1152
				kp->next = r->cookienmap[i];
1153
				r->cookienmap[i] = kp;
1154
				return;
1155
			}
1156
	}
1157
}
1158

1159
void
1160
khttp_child_free(struct kreq *req)
1161
{
1162

1163
	kdata_free(req->kdata, 0);
1164
	req->kdata = NULL;
1165
	kreq_free(req);
1166
}
1167

1168
void
1169
khttp_free(struct kreq *req)
1170
{
1171

1172
	kdata_free(req->kdata, 1);
1173
	req->kdata = NULL;
1174
	kreq_free(req);
1175
}
1176

1177
/*
1178
 * Trim leading and trailing whitespace from a word.
1179
 * Note that this returns a pointer within "val" and optionally sets the
1180
 * NUL-terminator, so don't free() the returned value.
1181
 */
1182
static char *
1183
trim(char *val)
1184
{
1185
	char	*cp;
1186

1187
	while (isspace((unsigned char)*val))
1188
		val++;
1189

1190
	cp = strchr(val, '\0') - 1;
1191
	while (cp > val && isspace((unsigned char)*cp))
1192
		*cp-- = '\0';
1193

1194
	return val;
1195
}
1196

1197
/*
1198
 * Simple email address validation: this is NOT according to the spec,
1199
 * but a simple heuristic look at the address.
1200
 * Note that this lowercases the mail address.
1201
 */
1202
static char *
1203
valid_email(char *p)
1204
{
1205
	char	*cp, *start;
1206
	size_t	 sz;
1207

1208
	/* 
1209
	 * Trim all white-space before and after.
1210
	 * Length check (min: a@b, max: 254 bytes).
1211
	 * Make sure we have an at-sign.
1212
	 * Make sure at signs aren't at the start or end.
1213
	 */
1214

1215
	cp = start = trim(p);
1216

1217
	if ((sz = strlen(cp)) < 3 || sz > 254)
1218
		return NULL;
1219
	if (cp[0] == '@' || cp[sz - 1] == '@')
1220
		return NULL;
1221
	if (strchr(cp, '@') == NULL)
1222
		return NULL;
1223

1224
	for (cp = start; *cp != '\0'; cp++)
1225
		*cp = tolower((unsigned char)*cp);
1226

1227
	return start;
1228
}
1229

1230
int
1231
kvalid_date(struct kpair *kp)
1232
{
1233
	int		 mday, mon, year;
1234

1235
	if (kp->valsz != 10 || kp->val[10] != '\0' ||
1236
	    !isdigit((unsigned char)kp->val[0]) ||
1237
	    !isdigit((unsigned char)kp->val[1]) ||
1238
	    !isdigit((unsigned char)kp->val[2]) ||
1239
	    !isdigit((unsigned char)kp->val[3]) ||
1240
	    kp->val[4] != '-' || 
1241
	    !isdigit((unsigned char)kp->val[5]) ||
1242
	    !isdigit((unsigned char)kp->val[6]) ||
1243
	    kp->val[7] != '-' || 
1244
	    !isdigit((unsigned char)kp->val[8]) ||
1245
	    !isdigit((unsigned char)kp->val[9]))
1246
		return 0;
1247

1248
	/* 
1249
	 * We know these are all positive integer values, so no need to
1250
	 * use the more complicated interface for this.
1251
	 */
1252

1253
	year = atoi(&kp->val[0]);
1254
	mon = atoi(&kp->val[5]);
1255
	mday = atoi(&kp->val[8]);
1256

1257
	if (!khttp_date2epoch(&kp->parsed.i, mday, mon, year))
1258
		return 0;
1259

1260
	kp->type = KPAIR_INTEGER;
1261
	return 1;
1262
}
1263

1264
int
1265
kvalid_stringne(struct kpair *p)
1266
{
1267
	/*
1268
	 * To check if we're a valid string, simply make sure that the
1269
	 * NUL-terminator is where we expect it to be.
1270
	 */
1271

1272
	if (strlen(p->val) != p->valsz || p->valsz == 0)
1273
		return 0;
1274
	p->type = KPAIR_STRING;
1275
	p->parsed.s = p->val;
1276
	return 1;
1277
}
1278

1279
int
1280
kvalid_string(struct kpair *p)
1281
{
1282
	/*
1283
	 * To check if we're a valid string, simply make sure that the
1284
	 * NUL-terminator is where we expect it to be.
1285
	 */
1286

1287
	if (strlen(p->val) != p->valsz)
1288
		return 0;
1289
	p->type = KPAIR_STRING;
1290
	p->parsed.s = p->val;
1291
	return 1;
1292
}
1293

1294
int
1295
kvalid_email(struct kpair *p)
1296
{
1297

1298
	if (!kvalid_stringne(p))
1299
		return 0;
1300
	return (p->parsed.s = valid_email(p->val)) != NULL;
1301
}
1302

1303
int
1304
kvalid_udouble(struct kpair *p)
1305
{
1306

1307
	return kvalid_double(p) && p->parsed.d > 0.0;
1308
}
1309

1310
int
1311
kvalid_double(struct kpair *p)
1312
{
1313
	char		*ep;
1314
	const char	*nval;
1315
	double		 lval;
1316
	int		 er;
1317

1318
	if (!kvalid_stringne(p))
1319
		return 0;
1320

1321
	/* 
1322
	 * We might get an empty string from trim, which constitutes a
1323
	 * valid double (!?), so double check that the string is
1324
	 * non-empty after trimming whitespace.
1325
	 * We trim white-space because strtod(3) accepts white-space
1326
	 * before but not after the string.
1327
	 */
1328

1329
	nval = trim(p->val);
1330
	if (nval[0] == '\0')
1331
		return 0;
1332

1333
	/* Save errno so we can restore it later. */
1334

1335
	er = errno;
1336
	errno = 0;
1337
	lval = strtod(nval, &ep);
1338
	if (errno == ERANGE)
1339
		return 0;
1340

1341
	/* Restore errno. */
1342

1343
	errno = er;
1344

1345
	if (*ep != '\0')
1346
		return 0;
1347

1348
	p->parsed.d = lval;
1349
	p->type = KPAIR_DOUBLE;
1350
	return 1;
1351
}
1352

1353
int
1354
kvalid_int(struct kpair *p)
1355
{
1356
	const char	*ep;
1357

1358
	if (!kvalid_stringne(p))
1359
		return 0;
1360
	p->parsed.i = strtonum
1361
		(trim(p->val), INT64_MIN, INT64_MAX, &ep);
1362
	p->type = KPAIR_INTEGER;
1363
	return ep == NULL;
1364
}
1365

1366
int
1367
kvalid_bit(struct kpair *p)
1368
{
1369

1370
	if (!kvalid_uint(p))
1371
		return 0;
1372
	return p->parsed.i <= 64;
1373
}
1374

1375
int
1376
kvalid_uint(struct kpair *p)
1377
{
1378
	const char	*ep;
1379

1380
	p->parsed.i = strtonum(trim(p->val), 0, INT64_MAX, &ep);
1381
	p->type = KPAIR_INTEGER;
1382
	return ep == NULL;
1383
}
1384

1385
enum kcgi_err
1386
kcgi_buf_write(const char *s, size_t sz, void *arg)
1387
{
1388
	struct kcgi_buf	*b = arg;
1389
	void		*pp;
1390

1391
	/* Let empty/NULL pass through. */
1392

1393
	if (s == NULL || sz == 0)
1394
		return KCGI_OK;
1395

1396
	/* Grow the buffer and leave some slop space. */
1397

1398
	if (b->sz + sz + 1 > b->maxsz) {
1399
		b->maxsz = b->sz + sz + 1 + 
1400
			(b->growsz == 0 ? 1024 : b->growsz);
1401
		if ((pp = kxrealloc(b->buf, b->maxsz)) == NULL)
1402
			return KCGI_ENOMEM;
1403
		b->buf = pp;
1404
	}
1405
	
1406
	/* Always NUL-terminate even though we accept binary data. */
1407

1408
	memcpy(&b->buf[b->sz], s, sz);
1409
	b->sz += sz;
1410
	b->buf[b->sz] = '\0';
1411
	return KCGI_OK;
1412
}
1413

1414
enum kcgi_err
1415
kcgi_buf_printf(struct kcgi_buf *buf, const char *fmt, ...)
1416
{
1417
	char		*nbuf;
1418
	int		 len;
1419
	va_list		 ap;
1420
	enum kcgi_err	 er;
1421

1422
	/* Let this bogus case pass through. */
1423

1424
	if (fmt == NULL)
1425
		return KCGI_OK;
1426

1427
	/* Allocate temporary buffer. */
1428

1429
	va_start(ap, fmt);
1430
	len = kxvasprintf(&nbuf, fmt, ap);
1431
	va_end(ap);
1432
	if (len == -1)
1433
		return KCGI_ENOMEM;
1434

1435
	/* Write and free. */
1436

1437
	er = kcgi_buf_write(nbuf, (size_t)len, buf);
1438
	free(nbuf);
1439
	return er;
1440
}
1441

1442
enum kcgi_err
1443
kcgi_buf_putc(struct kcgi_buf *buf, char c)
1444
{
1445

1446
	return kcgi_buf_write(&c, 1, buf);
1447
}
1448

1449
enum kcgi_err
1450
kcgi_buf_puts(struct kcgi_buf *buf, const char *cp)
1451
{
1452

1453
	if (cp == NULL)
1454
		return KCGI_OK;
1455
	return kcgi_buf_write(cp, strlen(cp), buf);
1456
}
1457

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.