geomyidae

A small C-based gopherd. (gopher://bitreich.org/1/scm/geomyidae)
git clone git://r-36.net/geomyidae
Log | Files | Refs | README | LICENSE

ind.c (10135B)


      1 /*
      2  * Copy me if you can.
      3  * by 20h
      4  */
      5 
      6 #include <unistd.h>
      7 #include <stdarg.h>
      8 #include <string.h>
      9 #include <memory.h>
     10 #include <fcntl.h>
     11 #include <stdio.h>
     12 #include <stdlib.h>
     13 #include <stdint.h>
     14 #include <time.h>
     15 #include <netdb.h>
     16 #include <sys/socket.h>
     17 #include <sys/stat.h>
     18 #include <netinet/in.h>
     19 #include <netinet/tcp.h>
     20 #include <arpa/inet.h>
     21 #include <sys/ioctl.h>
     22 #include <limits.h>
     23 
     24 #include "arg.h"
     25 #include "ind.h"
     26 #include "handlr.h"
     27 
     28 /*
     29  * Be careful, to look at handlerequest(), in case you add any executing
     30  * handler, so nocgi will be valuable.
     31  */
     32 
     33 filetype type[] = {
     34 	{"default", "9", handlebin},
     35 	{"gph", "1", handlegph},
     36 	{"cgi", "0", handlecgi},
     37 	{"dcgi", "1", handledcgi},
     38 	{"bin", "9", handlebin},
     39 	{"tgz", "9", handlebin},
     40 	{"gz", "9", handlebin},
     41 	{"jpg", "I", handlebin},
     42 	{"gif", "g", handlebin},
     43 	{"png", "I", handlebin},
     44 	{"bmp", "I", handlebin},
     45 	{"txt", "0", handlebin},
     46 	{"html", "0", handlebin},
     47 	{"htm", "0", handlebin},
     48 	{"xhtml", "0", handlebin},
     49 	{"css", "0", handlebin},
     50 	{"md", "0", handlebin},
     51 	{"c", "0", handlebin},
     52 	{"sh", "0", handlebin},
     53 	{"patch", "0", handlebin},
     54 	{"meme", "0", handlebin},
     55 	{NULL, NULL, NULL},
     56 };
     57 
     58 int
     59 pendingbytes(int sock)
     60 {
     61 	int pending, rval;
     62 
     63 	pending = 0;
     64 	rval = 0;
     65 #if defined(TIOCOUTQ) && !defined(__OpenBSD__)
     66 	rval = ioctl(sock, TIOCOUTQ, &pending);
     67 #else
     68 #ifdef SIOCOUTQ
     69 	rval = ioctl(sock, SIOCOUTQ, &pending);
     70 #endif
     71 #endif
     72 
     73 	if (rval != 0)
     74 		return 0;
     75 
     76 	return pending;
     77 }
     78 
     79 void
     80 waitforpendingbytes(int sock)
     81 {
     82 	int npending = 0, opending = 0;
     83 	useconds_t trytime = 10;
     84 
     85 	/*
     86 	 * Wait until there is nothing pending or the connection stalled
     87 	 * (nothing was sent) for around 40 seconds. Beware, trytime is
     88 	 * an exponential wait.
     89 	 */
     90 	while ((npending = pendingbytes(sock)) > 0 && trytime < 20000000) {
     91 		if (opending != 0) {
     92 			if (opending != npending) {
     93 				trytime = 10;
     94 			} else {
     95 				/*
     96 				 * Exponentially increase the usleep
     97 				 * waiting time to not waste CPU
     98 				 * resources.
     99 				 */
    100 				trytime += trytime;
    101 			}
    102 		}
    103 		opending = npending;
    104 
    105 		usleep(trytime);
    106 	}
    107 }
    108 
    109 int
    110 xsendfile(int fd, int sock)
    111 {
    112 	struct stat st;
    113 	char *sendb, *sendi;
    114 	size_t bufsiz = BUFSIZ;
    115 	int len, sent, optval;
    116 
    117 	USED(optval);
    118 
    119 	if (fstat(fd, &st) >= 0)
    120 		if ((bufsiz = st.st_blksize) < BUFSIZ)
    121 			bufsiz = BUFSIZ;
    122 
    123 	sendb = xmalloc(bufsiz);
    124 	while ((len = read(fd, sendb, bufsiz)) > 0) {
    125 		sendi = sendb;
    126 		while (len > 0) {
    127 			if ((sent = write(sock, sendi, len)) < 0) {
    128 				free(sendb);
    129 				return -1;
    130 			}
    131 			len -= sent;
    132 			sendi += sent;
    133 		}
    134 	}
    135 	free(sendb);
    136 	return 0;
    137 }
    138 
    139 void *
    140 xcalloc(size_t nmemb, size_t size)
    141 {
    142 	void *p;
    143 
    144 	if (!(p = calloc(nmemb, size))) {
    145 		perror("calloc");
    146 		exit(1);
    147 	}
    148 
    149 	return p;
    150 }
    151 
    152 void *
    153 xmalloc(size_t size)
    154 {
    155 	void *p;
    156 
    157 	if (!(p = malloc(size))) {
    158 		perror("malloc");
    159 		exit(1);
    160 	}
    161 
    162 	return p;
    163 }
    164 
    165 void *
    166 xrealloc(void *ptr, size_t size)
    167 {
    168 	if (!(ptr = realloc(ptr, size))) {
    169 		perror("realloc");
    170 		exit(1);
    171 	}
    172 
    173 	return ptr;
    174 }
    175 
    176 char *
    177 xstrdup(const char *str)
    178 {
    179 	char *ret;
    180 
    181 	if (!(ret = strdup(str))) {
    182 		perror("strdup");
    183 		exit(1);
    184 	}
    185 
    186 	return ret;
    187 }
    188 
    189 filetype *
    190 gettype(char *filename)
    191 {
    192 	char *end;
    193 	int i;
    194 
    195 	end = strrchr(filename, '.');
    196 	if (end == NULL)
    197 		return &type[0];
    198 	end++;
    199 
    200 	for (i = 0; type[i].end != NULL; i++)
    201 		if (!strcasecmp(end, type[i].end))
    202 			return &type[i];
    203 
    204 	return &type[0];
    205 }
    206 
    207 void
    208 freeelem(Elems *e)
    209 {
    210 	if (e != NULL) {
    211 		if (e->e != NULL) {
    212 			for (;e->num > 0; e->num--)
    213 				if (e->e[e->num - 1] != NULL)
    214 					free(e->e[e->num - 1]);
    215 			free(e->e);
    216 		}
    217 		free(e);
    218 	}
    219 	return;
    220 }
    221 
    222 void
    223 freeindex(Indexs *i)
    224 {
    225 	if (i != NULL) {
    226 		if (i->n != NULL) {
    227 			for (;i->num > 0; i->num--)
    228 				freeelem(i->n[i->num - 1]);
    229 			free(i->n);
    230 		}
    231 		free(i);
    232 	}
    233 
    234 	return;
    235 }
    236 
    237 void
    238 addelem(Elems *e, char *s)
    239 {
    240 	e->num++;
    241 	e->e = xrealloc(e->e, sizeof(char *) * e->num);
    242 	e->e[e->num - 1] = xstrdup(s);
    243 
    244 	return;
    245 }
    246 
    247 Elems *
    248 getadv(char *str)
    249 {
    250 	char *b, *e, *o, *bo;
    251 	Elems *ret;
    252 
    253 	ret = xcalloc(1, sizeof(Elems));
    254 
    255 	if (strchr(str, '\t')) {
    256 		addelem(ret, "i");
    257 		addelem(ret, "Happy helping ☃ here: You tried to "
    258 			"output a spurious tab character. This will "
    259 			"break gopher. Please review your scripts. "
    260 			"Have a nice day!");
    261 		addelem(ret, "Err");
    262 		addelem(ret, "server");
    263 		addelem(ret, "port");
    264 
    265 		return ret;
    266 	}
    267 
    268 	if (str[0] == '[') {
    269 		o = xstrdup(str);
    270 		b = o + 1;
    271 		bo = b;
    272 		while ((e = strchr(bo, '|')) != NULL) {
    273 			if (e != bo && e[-1] == '\\') {
    274 				memmove(&e[-1], e, strlen(e));
    275 				bo = e;
    276 				continue;
    277 			}
    278 			*e = '\0';
    279 			e++;
    280 			addelem(ret, b);
    281 			b = e;
    282 			bo = b;
    283 		}
    284 
    285 		e = strchr(b, ']');
    286 		if (e != NULL) {
    287 			*e = '\0';
    288 			addelem(ret, b);
    289 		}
    290 		free(o);
    291 
    292 		if (ret->e != NULL && ret->e[0] != NULL && ret->e[0][0] == '\0') {
    293 			freeelem(ret);
    294 			ret = xcalloc(1, sizeof(Elems));
    295 
    296 			addelem(ret, "i");
    297 			addelem(ret, "Happy helping ☃ here: You did not "
    298 				"specify an item type on this line. Please "
    299 				"review your scripts. "
    300 				"Have a nice day!");
    301 			addelem(ret, "Err");
    302 			addelem(ret, "server");
    303 			addelem(ret, "port");
    304 
    305 			return ret;
    306 		}
    307 
    308 		if (ret->e != NULL && ret->num == 5)
    309 			return ret;
    310 
    311 		/* Invalid entry: Give back the whole line. */
    312 		freeelem(ret);
    313 		ret = xcalloc(1, sizeof(Elems));
    314 	}
    315 
    316 	b = str;
    317 	if (*str == 't')
    318 		b++;
    319 	addelem(ret, "i");
    320 	addelem(ret, b);
    321 	addelem(ret, "Err");
    322 	addelem(ret, "server");
    323 	addelem(ret, "port");
    324 
    325 	return ret;
    326 }
    327 
    328 void
    329 addindexs(Indexs *idx, Elems *el)
    330 {
    331 	idx->num++;
    332 	idx->n = xrealloc(idx->n, sizeof(Elems) * idx->num);
    333 	idx->n[idx->num - 1] = el;
    334 
    335 	return;
    336 }
    337 
    338 Indexs *
    339 scanfile(char *fname)
    340 {
    341 	char *ln = NULL;
    342 	size_t linesiz = 0;
    343 	ssize_t n;
    344 	FILE *fp;
    345 	Indexs *ret;
    346 	Elems *el;
    347 
    348 	if (!(fp = fopen(fname, "r")))
    349 		return NULL;
    350 
    351 	ret = xcalloc(1, sizeof(Indexs));
    352 
    353 	while ((n = getline(&ln, &linesiz, fp)) > 0) {
    354 		if (ln[n - 1] == '\n')
    355 			ln[--n] = '\0';
    356 		el = getadv(ln);
    357 		if(el == NULL)
    358 			continue;
    359 
    360 		addindexs(ret, el);
    361 	}
    362 	if (ferror(fp))
    363 		perror("getline");
    364 	free(ln);
    365 	fclose(fp);
    366 
    367 	if (ret->n == NULL) {
    368 		free(ret);
    369 		return NULL;
    370 	}
    371 
    372 	return ret;
    373 }
    374 
    375 int
    376 printelem(int fd, Elems *el, char *file, char *base, char *addr, char *port)
    377 {
    378 	char *path, *p, buf[PATH_MAX+1];
    379 	int len;
    380 
    381 	if (!strcmp(el->e[3], "server")) {
    382 		free(el->e[3]);
    383 		el->e[3] = xstrdup(addr);
    384 	}
    385 	if (!strcmp(el->e[4], "port")) {
    386 		free(el->e[4]);
    387 		el->e[4] = xstrdup(port);
    388 	}
    389 
    390 	/*
    391 	 * Ignore if the path is from base, if it might be some h type with
    392 	 * some URL and ignore various types that have different semantics but
    393 	 * to point to some file or directory.
    394 	 */
    395 	if ((el->e[2][0] != '\0'
    396 	    && el->e[2][0] != '/'
    397 	    && el->e[0][0] != 'i'
    398 	    && el->e[0][0] != '2'
    399 	    && el->e[0][0] != '3'
    400 	    && el->e[0][0] != '8'
    401 	    && el->e[0][0] != 'w'
    402 	    && el->e[0][0] != 'T') &&
    403 	    !(el->e[0][0] == 'h' && !strncmp(el->e[2], "URL:", 4))) {
    404 		path = file + strlen(base);
    405 		if ((p = strrchr(path, '/')))
    406 			len = p - path;
    407 		else
    408 			len = strlen(path);
    409 		snprintf(buf, sizeof(buf), "%s%.*s/%s", base, len, path, el->e[2]);
    410 
    411 		if ((path = realpath(buf, NULL)) &&
    412 				!strncmp(base, path, strlen(base))) {
    413 			p = path + strlen(base);
    414 			free(el->e[2]);
    415 			el->e[2] = xstrdup(p[0]? p : "/");
    416 		}
    417 		free(path);
    418 	}
    419 
    420 	if (dprintf(fd, "%.1s%s\t%s\t%s\t%s\r\n", el->e[0], el->e[1], el->e[2],
    421 			el->e[3], el->e[4]) < 0) {
    422 		perror("printelem: dprintf");
    423 		return -1;
    424 	}
    425 	return 0;
    426 }
    427 
    428 char *
    429 smprintf(char *fmt, ...)
    430 {
    431         va_list fmtargs;
    432         char *ret;
    433         int size;
    434 
    435         va_start(fmtargs, fmt);
    436         size = vsnprintf(NULL, 0, fmt, fmtargs);
    437         va_end(fmtargs);
    438 
    439         ret = xcalloc(1, ++size);
    440         va_start(fmtargs, fmt);
    441         vsnprintf(ret, size, fmt, fmtargs);
    442         va_end(fmtargs);
    443 
    444         return ret;
    445 }
    446 
    447 char *
    448 reverselookup(char *host)
    449 {
    450 	struct in_addr hoststr;
    451 	struct hostent *client;
    452 	char *rethost;
    453 
    454 	rethost = NULL;
    455 
    456 	if (inet_pton(AF_INET, host, &hoststr)) {
    457 		client = gethostbyaddr((const void *)&hoststr,
    458 				sizeof(hoststr), AF_INET);
    459 		if (client != NULL)
    460 			rethost = xstrdup(client->h_name);
    461 	}
    462 
    463 	if (rethost == NULL)
    464 		rethost = xstrdup(host);
    465 
    466 	return rethost;
    467 }
    468 
    469 void
    470 setcgienviron(char *file, char *path, char *port, char *base, char *args,
    471 		char *sear, char *ohost, char *chost, int istls)
    472 {
    473 	/*
    474 	 * TODO: Clean environment from possible unsafe environment variables.
    475 	 *       But then it is the responsibility of the script writer.
    476 	 */
    477 	unsetenv("AUTH_TYPE");
    478 	unsetenv("CONTENT_LENGTH");
    479 	unsetenv("CONTENT_TYPE");
    480 	setenv("GATEWAY_INTERFACE", "CGI/1.1", 1);
    481 	/* TODO: Separate, if run like rest.dcgi. */
    482 	setenv("PATH_INFO", file, 1);
    483 	setenv("PATH_TRANSLATED", path, 1);
    484 
    485 	setenv("QUERY_STRING", args, 1);
    486 	/* legacy compatibility */
    487 	setenv("SELECTOR", args, 1);
    488 	setenv("REQUEST", args, 1);
    489 
    490 	setenv("REMOTE_ADDR", chost, 1);
    491 	/*
    492 	 * Don't do a reverse lookup on every call. Only do when needed, in
    493 	 * the script. The RFC allows us to set the IP to the value.
    494 	 */
    495 	setenv("REMOTE_HOST", chost, 1);
    496 	/* Please do not implement identd here. */
    497 	unsetenv("REMOTE_IDENT");
    498 	unsetenv("REMOTE_USER");
    499 	/* Make PHP happy. */
    500 	setenv("REDIRECT_STATUS", "", 1);
    501 	/*
    502 	 * Only GET is possible in gopher. POST emulation would be really
    503 	 * ugly.
    504 	 */
    505 	setenv("REQUEST_METHOD", "GET", 1);
    506 	setenv("SCRIPT_NAME", file, 1);
    507 	setenv("SERVER_NAME", ohost, 1);
    508 	setenv("SERVER_PORT", port, 1);
    509 	setenv("SERVER_PROTOCOL", "gopher/1.0", 1);
    510 	setenv("SERVER_SOFTWARE", "geomyidae", 1);
    511 
    512 	setenv("X_GOPHER_SEARCH", sear, 1);
    513 	/* legacy compatibility */
    514 	setenv("SEARCHREQUEST", sear, 1);
    515 
    516 	if (istls) {
    517 		setenv("GOPHERS", "on", 1);
    518 		setenv("HTTPS", "on", 1);
    519 	} else {
    520 		unsetenv("GOPHERS");
    521 		unsetenv("HTTPS");
    522 	}
    523 
    524 }
    525 
    526 char *
    527 humansize(off_t n)
    528 {
    529 	static char buf[16];
    530 	const char postfixes[] = "BKMGTPE";
    531 	double size;
    532 	int i = 0;
    533 
    534 	for (size = n; size >= 1024 && i < strlen(postfixes); i++)
    535 		size /= 1024;
    536 
    537 	if (!i) {
    538 		snprintf(buf, sizeof(buf), "%ju%c", (uintmax_t)n,
    539 				postfixes[i]);
    540 	} else {
    541 		snprintf(buf, sizeof(buf), "%.1f%c", size, postfixes[i]);
    542 	}
    543 
    544 	return buf;
    545 }
    546 
    547 char *
    548 humantime(const time_t *clock)
    549 {
    550 	static char buf[32];
    551 	struct tm *tm;
    552 
    553 	tm = localtime(clock);
    554 	strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M %Z", tm);
    555 
    556 	return buf;
    557 }
    558