#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

enum {
	NPOSTS = 2,
	TIMEOUT = 5
};

FILE *dial(const char *host, const char *port);

char **tokenise(const char *s, const char *delim);

size_t count_tokens(const char *s, const char *delim);

char *nth_token(char *to, size_t n, const char *s, const char *delim, size_t i);

void free_tokens(char **a);

static char buffer[2000000];

void post(const char *thread, const char *name, const char *email,
	  const char *comment)
{
	FILE *iop;

	if (!(iop = dial("dis.4chan.org", "80"))) {
		fputs("error: unable to connect to server\n", stderr);
		return;
	}
	sprintf(buffer, "bbs=prog&id=%s&shiichan=proper2&"
		"kotehan=%s&meiru=%s&com=%s&email=%%27", thread, name, email,
		comment);
	fprintf(iop,
		"POST /post HTTP/1.1\r\n"
		"Host: dis.4chan.org\r\n"
		"Connection: close\r\n"
		"Content-Type: application/x-www-form-urlencoded\r\n"
		"Content-Length: %lu\r\n\r\n%s",
		(unsigned long int) strlen(buffer), buffer);
	fflush(iop);
	getc(iop);
	fclose(iop);
	puts(thread);
}

char *get(const char *page)
{
	FILE *iop;
	size_t n;

	puts("Connecting to server ...");
	if (!(iop = dial("dis.4chan.org", "80"))) {
		fputs("error: unable to connect to server\n", stderr);
		exit(EXIT_FAILURE);
	}
	fprintf(iop,
		"GET %s HTTP/1.1\r\n"
		"Host: dis.4chan.org\r\n"
		"Connection: close\r\n\r\n", page);
	fflush(iop);
	puts("Downloading thread list ...");
	n = fread(buffer, 1, sizeof buffer, iop);
	buffer[n] = '\0';
	fclose(iop);
	return buffer;
}

int main(int argc, char *argv[])
{
	char buf[1024];
	size_t n, i;
	char **a;

	srand(time(0));
	if (!(a = tokenise(get("/prog/subject.txt"), "\n"))) {
		fputs("memory error\n", stderr);
		return EXIT_FAILURE;
	}
	for (n = i = 0; (a[n] = a[i]); i++)
		if (count_tokens(a[i], "<>") == 7)
			n++;
	puts("Starting ...");
	for (i = 0;;) {
		post(nth_token(buf, sizeof buf, a[rand() % n],
			       "<>", 3), "James+Gosling", "",
		     "GAWWZMACS+FLABBERGASTS+MY+AUDIENCE");
		if (++i == NPOSTS)
			break;
		sleep(TIMEOUT);
	}
	free_tokens(a);
	return 0;
}

/* -------------------------------------------------------------------------- */

char *nth_token(char *to, size_t n, const char *s, const char *delim, size_t i)
{
	size_t dsize = strlen(delim);
	char *save = to;

	while (i--) {
		if (!(s = strstr(s, delim)))
			return 0;
		s += dsize;
	}
	while (--n && (*to = *s) && strncmp(s++, delim, dsize))
		to++;
	*to = '\0';
	return save;
}

size_t count_tokens(const char *s, const char *delim)
{
	size_t dsize = strlen(delim), n = 0;

	while (*s) { 
		n++;
		while (*s && strncmp(s, delim, dsize))
			s++;
		if (!*s)
			break;
		s += dsize;
	}
	return n;
}

void free_tokens(char **a)
{
	free(*--a);
	free(a);
}

static char **make_tokens(char **to, size_t n, char *s, const char *delim)
{
	char **save = to, **endp = to + n - 1;
	size_t dsize = strlen(delim);

	while (*s) {
		if (to < endp)
			*to++ = s;
		while (*s && strncmp(s, delim, dsize))
			s++;
		if (!*s)
			break;
		*s = '\0';
		s += dsize;
	}
	*to = 0;
	return save;
}

char **tokenise(const char *s, const char *delim)
{
	size_t n = count_tokens(s, delim) + 1;
	char **r;

	if (!(r = malloc((n + 1) * sizeof *r)))
		return 0;
	if (!(*r = malloc(strlen(s) + 1))) {
		free(r);
		return 0;
	}
	return make_tokens(r + 1, n, strcpy(*r, s), delim);
}

FILE *dial(const char *host, const char *port)
{
	struct addrinfo hints;
	struct addrinfo *info, *ip;
	int fd;

	memset(&hints, 0, sizeof hints);
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_family = AF_UNSPEC;
	if (getaddrinfo(host, port, &hints, &info))
		return 0;
	for (ip = info; ip; ip = ip->ai_next) {
		if ((fd = socket(ip->ai_family, ip->ai_socktype,
				 ip->ai_protocol)) == -1)
			continue;
		if (!connect(fd, ip->ai_addr, ip->ai_addrlen))
			break;
		close(fd);
	}
	freeaddrinfo(info);
	return !ip ? 0 : fdopen(fd, "r+");
}