#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <netdb.h>
#include <string.h>

/* #define DEBUG */

#define PORT 8181
#define USERAGENT "HTMLGET 1.0"
#define BUFSIZE 65535

void show_buffer(unsigned char *buf, int len)
{
	int i, j;
	char str[17];

	str[16] = 0;
	printf("\n");
	for (i = 0; i < len; i += 16) {
		fprintf(stdout, "   ");
		for (j = 0; j < 16 && i + j < len; j++) {
			fprintf(stdout, "%02x ", buf[i + j]);

			if (buf[i + j] > 32 && buf[i + j] < 127)
				str[j] = buf[i + j];
			else
				str[j] = '.';
		}
		for (; j < 16; j++) {
			fprintf(stdout, "   ", buf[i + j]);
			str[j] = ' ';
		}
		printf("   |   %s   |\n", str);
	}
	fflush(stdout);
}

int sendall(int s, char *buf, int *len)
{
	int total = 0;		// how many bytes we've sent
	int bytesleft = *len;	// how many we have left to send
	int n;

	while (total < *len) {
		n = write(s, buf + total, bytesleft, 0);
		/* fprintf(stderr, "sent: %d\n", n); */
		if (n == -1) {
			break;
		}
		total += n;
		bytesleft -= n;

	}

	*len = total;		// return number actually sent here

	return n == -1 ? -1 : 0;	// return -1 on failure, 0 on success
}

int create_tcp_socket()
{
	int sock;

	if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
		perror("Can't create TCP socket");
		exit(EXIT_FAILURE);
	}

	return sock;
}

char *get_ip(char *host)
{
	struct hostent *hent;
	int iplen = 15;		/* XXX.XXX.XXX.XXX */
	char *ip = (char *)malloc(iplen + 1);

	memset(ip, 0, iplen + 1);

	if ((hent = gethostbyname(host)) == NULL) {
		herror("Can't get IP");
		exit(EXIT_FAILURE);
	}

	if (inet_ntop(AF_INET, (void *)hent->h_addr_list[0], ip, iplen) == NULL) {
		perror("Can't resolve host");
		exit(EXIT_FAILURE);
	}

	return ip;
}

char *build_get_query(char *host, char *page)
{
	char *query;
	char *getpage = page;
	char *tpl = "GET /%s HTTP/1.0\r\nHost: %s\r\nUser-Agent: %s\r\n\r\n";

	if (getpage[0] == '/') {
		getpage = getpage + 1;
		fprintf(stderr, "Removing leading \"/\", converting %s to %s\n",
			page, getpage);
	}
	// -5 is to consider the %s %s %s in tpl and the ending \0
	query =
	    (char *)malloc(strlen(host) + strlen(getpage) + strlen(USERAGENT) +
			   strlen(tpl) - 5);
	sprintf(query, tpl, getpage, host, USERAGENT);
	return query;
}

void usage()
{
	fprintf(stderr, "USAGE: htmlget host [page]\n\
		\thost: the website hostname. ex: coding.debuntu.org\n\
		\tpage: the page to retrieve. ex: index.html, default: /\n");
}

char *get_url()
{
	size_t len;
	char *url;
	char str[BUFSIZE] = { 0 };
	fgets(str, BUFSIZE - 1, stdin);

	/* Ripping off Enter key */
	len = strlen(str);
	if (str[len - 1] == 10)
		str[len - 1] = 0;

	url = (char *)malloc(len);
	strncpy(url, str, len);

	return url;
}

int main(int argc, char **argv)
{
	struct sockaddr_in *remote;
	int sock;
	int tmpres;
	char *ip;
	char *get;
	char buf[BUFSIZE + 1];
	char *host;
	char *page;
	char *url;
	int i = 1;
	int len;

	if (argc == 1) {
		usage();
		exit(2);
	}

	host = argv[1];
	ip = get_ip(host);
	fprintf(stderr, "Sending data to %s\n", ip);

	while (1) {

		url = get_url();

		sock = create_tcp_socket();

		remote =
		    (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in *));
		remote->sin_family = AF_INET;
		tmpres =
		    inet_pton(AF_INET, ip,
			      (void *)(&(remote->sin_addr.s_addr)));
		if (tmpres < 0) {
			perror("Can't set remote->sin_addr.s_addr");
			exit(EXIT_FAILURE);
		} else if (tmpres == 0) {
			fprintf(stderr, "%s is not a valid IP address\n", ip);
			exit(EXIT_FAILURE);
		}
		remote->sin_port = htons(PORT);

		if (connect
		    (sock, (struct sockaddr *)remote,
		     sizeof(struct sockaddr)) < 0) {
			perror("Could not connect");
			exit(1);
		}

		page = url;
		get = build_get_query(host, page);
		// fprintf(stderr, "Query is:\n<<START>>\n%s<<END>>\n", get);
#ifdef DEBUG
		fprintf(stderr, "--\n%s\n", url);
#endif

		//Send the query to the server
		int sent = 0;
#ifdef DEBUG
		fprintf(stderr, "packet num: %d\n", i);
#endif
		len = strlen(get);
		sendall(sock, get, &len);
/*
	while(sent < strlen(get)) {
		tmpres = send(sock, get+sent, strlen(get)-sent, 0);
		if(tmpres == -1) {
			perror("Can't send query");
			exit(1);
		}
		sent += tmpres;
		fprintf(stderr, "sent: %d\n", tmpres);
		if (sent < strlen(get)) fprintf(stderr, "\nLOOP\n");
	}
*/

		//now it is time to receive the page
		memset(buf, 0, sizeof(buf));
		int htmlstart = 0;
		char *htmlcontent;
		/* while((tmpres = read(sock, buf, BUFSIZE)) > 0) { */
		while ((tmpres = recv(sock, buf, BUFSIZE, 0)) > 0) {
#ifdef DEBUG
			show_buffer(buf, strlen(buf));
#endif
			if (htmlstart == 0) {
				// Under certain conditions this will not work.
				// If the \r\n\r\n part is splitted into two messages
				// it will fail to detect the beginning of HTML content
				//
				htmlcontent = strstr(buf, "\r\n\r\n");
				if (htmlcontent != NULL) {
					htmlstart = 1;
					htmlcontent += 4;
				}

			} else {
				htmlcontent = buf;
			}
#ifdef DEBUG
			if (htmlstart) {
				fprintf(stdout, "\n::::HTML::::\n");
				fprintf(stdout, htmlcontent);
				fprintf(stdout, "\n------------\n");
			}
#endif

			memset(buf, 0, tmpres);
		}

		free(get);
		free(url);
		free(remote);
		close(sock);
		i++;
	}
	free(ip);
	return 0;
}