/* buildd_socker.c
   Dispatch incoming requests to :::8010 so people who are behind firewalls
   can see buildd.

  Copyright (c) 2008 Bernhard Fischer
  Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>

#define THE_PORT "8010"

#if !defined BUFSIZ
#define BUFSIZ (256 * sizeof(void*))
#endif
static void xperror(char *str)
{
	perror (str);
	exit (1);
}
int main (int argc, char **argv, char **argenv)
{
	struct addrinfo hint, *addr;
	int srv, fd, num, skip;
	ssize_t bytes;
	fd_set rd;
	const int one = 1;
	char *buf = malloc (BUFSIZ), **req, *ptr;
	const char contentt[] = "Content-Type: ";
	unsigned state = 0;

	if (buf == NULL)
		xperror ("malloc");

	req = argenv;
	while (*req) {
		if (strncmp("QUERY_STRING=", *req, 13) == 0) {
			if (!*req || strlen(*req) < 13)
				goto err;
			*req += 13;
			state = 1;
			break;
		}
		++req;
	}
	if (!state || !*req || strlen (*req) > BUFSIZ - 16)
		goto err;
	state = 0;
	memset(&hint, 0 , sizeof(hint));
	hint.ai_socktype = SOCK_STREAM;
	hint.ai_protocol = IPPROTO_TCP;
	hint.ai_flags = AI_NUMERICSERV;

	if (getaddrinfo ("localhost", THE_PORT, &hint, &addr))
		xperror ("getaddrinfo");
	srv = socket (addr->ai_family, addr->ai_socktype, addr->ai_protocol);
	if (srv < 0)
		xperror ("socket");
	if (connect (srv, addr->ai_addr, addr->ai_addrlen))
		xperror ("connect");
	setsockopt (srv, SOL_SOCKET, SO_REUSEADDR, &one, sizeof (one));

	FD_ZERO (&rd);
	FD_SET (srv, &rd);

	num = sprintf (buf, "GET %s HTTP/1.0\n\n", *req);
	do {
		bytes = write (srv, buf, num);
	} while (bytes < 0 && errno == EINTR);
	fsync (srv);

	while (!(state & 1)) {
		if (select (FD_SETSIZE, &rd, NULL, NULL, NULL) < 0)
			xperror ("select");
		for (fd = 0; fd < FD_SETSIZE; fd++) {
			if (FD_ISSET (fd, &rd)) {
				do {
					bytes = read (fd, buf, BUFSIZ);
				} while (bytes < 0 && errno == EINTR);

				if (bytes <= 0) {
					state |= 1;
					break;
				}
				if (!(state & 2)) {
					ptr = strstr (buf, contentt);
					if (ptr == NULL) /*Super-long header?*/
						continue;
					skip = ptr - buf;
					state |= 2;
				} else
					skip = 0;
				do {
					num = write (STDOUT_FILENO, buf + skip,
							bytes - skip);
				} while (num < 0 && errno == EINTR);
			}

		}
	}
	freeaddrinfo (addr);
err:
	free (buf);
	return 0;
}
