/* Sockme.c -- Windows sockets sample application */
/*% cl sockme.c wsock32.lib */

#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#include <fcntl.h>

char *note[]= {
	"Usage: [Options] URL",
	"Print the URL (HTTP access only)",
	"URL may be of the form username:password@host/page.html",
	"Options:",
	"-h               Just get the headers [HEAD instead of GET]",
	"-x file          POST file to the URL",
	"-s fname         Save return as file fname [print headers on console]",
	"-p proxy:port    Use a proxy with the given port",
	"-a fname         Add the contents of file fname to the header being sent",
	"-v               Verbose",
	0};

/* Global buffers */
char buff[2048];
char ahost[256];
char *outname;

struct sockaddr_in anaddr;

u_short httpport= 80;	/* Port for http */
int app_closing;
WSADATA wskData;
struct hostent *hp;

/* Sockets  */
SOCKET fclient = INVALID_SOCKET;

void errormsg(char *msg){
	int err= WSAGetLastError();
	if(msg)
		fprintf(stderr, "%s [%d]\n", msg, err);
	/* Close any open sockets */
	if(fclient != INVALID_SOCKET)
		closesocket(fclient);

	/* Let windows sockets know we're done */
	WSACleanup();

	exit(msg? 5:0);
}
static char *encode(char *outstg, char *stg){
	/* Base64 encoding without CRLF every 64 (or 72) characters--
	 * (just for a short string)
	 * Note--out must be 4/3 longer than stg */
	int i, n;
	unsigned char igroup[3];
	unsigned char dtable[64];
	char *cp= outstg;

	/* Fill dtable with character encodings.  */
	for(i= 0; i < 26; i++){
		dtable[i]= 'A' + i;
		dtable[26 + i]= 'a' + i;
	}
	for(i= 0; i < 10; i++){
		dtable[52 + i]= '0' + i;
	}
	dtable[62]= '+';
	dtable[63]= '/';

	while(*stg){
		igroup[0]= 0; igroup[1]= 0; igroup[2]= 0;
		for(n= 0; n<3 && *stg; n++)
			igroup[n]= *stg++;
		cp[0]= dtable[igroup[0] >> 2];
		cp[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
		cp[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
		cp[3]= dtable[igroup[2] & 0x3F];

		/* Replace characters in output stream with "=" pad
		 * characters if fewer than three characters were read
		 * from the end of the input stream.
		 */
		while(n<3)
			cp[++n]= '=';
		cp += 4;
	}
	*cp= 0;
	return outstg;
}
void do_send(SOCKET s, char *buff, int nb){
	while(nb>0){
		int cnt= send(s, buff, nb, 0);
		if(cnt == SOCKET_ERROR)
			errormsg("Error sending string to http daemon");
		nb -= cnt;
		buff += cnt;
	}
}
void main(int ac, char **av){
	FILE *fpout= stdout;
	FILE *fppost= 0;
	FILE *fp;
	int ntot= 0;
	int cnt;
	char *command= "GET";
	char *proxy= 0;
	char *host= ahost;
	char *url= 0;
	char *user= 0;
	int verbose= 0;
	char *headextra;
	char *poster= 0;
	char *bufp;
	unsigned long lAddr;

	while(*++av)if(**av=='-')switch(av[0][1]){
	case 'x':	poster= *++av; command= "POST"; break;
	case 'a':	headextra= *++av; break;
	case 'v':	verbose= 1; break;
	case 'h':	command= "HEAD"; break;
	case 's':	outname= *++av; break;
	case 'p':	proxy= *++av; break;
	default:	
	err:
			for(av= note; *av; ++av)
				fprintf(stderr, "%s\n", *av);
			exit(5);
	}else if(!url)
		url= *av;
	else
		goto err;
	if(!url)
		goto err;

	if(command[0]=='H')	/* No output file with the HEAD command */
		outname= 0;

	_setmode(1, _O_BINARY);	/* May not be receiving text */

	if(proxy){
		strcpy(host, proxy);
	}else{
		if(!strnicmp(url, "HTTP://", 7))
			url += 7;
		strcpy(host, url);
		if(bufp= strchr(host, '/')){
			url= bufp;
			*url= 0;
		}else
			url= "/";
	}
	/* Check for user:password@host */
	if(bufp= strchr(host, '@')){
		user= host;
		*bufp++= 0;
		host= bufp;
	}
	if(bufp= strchr(host, ':')){
		*bufp++= 0;
		httpport= atoi(bufp);
	}
	if(WSAStartup(MAKEWORD(2,0), &wskData))
		return;

	/* Try numeric ip, then lookup host name */
	lAddr= inet_addr(host); 
	if(lAddr == INADDR_NONE){
		if((hp = gethostbyname(host)) == NULL)
			errormsg("Unknown host");
		memcpy((char *) &anaddr.sin_addr.s_addr, hp->h_addr, hp->h_length);
	}else
		memcpy((char *) &anaddr.sin_addr.s_addr, &lAddr, sizeof(lAddr));

	/* Look up port number (this seems to fail for http) */
	if(httpport == 0){
		struct servent *sp;
		if((sp= getservbyname("http", NULL)) == NULL)
			errormsg("Cannot determine port number for http daemon.");
		httpport = htons(sp->s_port);
	}
	/* Create socket */
	if((fclient= socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
		errormsg("Unable to create a socket");

	/* Fill in address to which we will connect */
	anaddr.sin_family = PF_INET;
	anaddr.sin_port = htons(httpport);

	/* Try to connect */
	if(connect(fclient, (struct sockaddr *)&anaddr, sizeof(struct sockaddr_in)))
		errormsg("Unable to connect to http daemon.");

	if(verbose)
		printf("...Connected to%s%s:%d\n", proxy? " proxy ":" ", host, httpport);

	/* Now construct the header to send */
	bufp= buff;
	if(!proxy)
		*url= '/';
	sprintf(bufp, "%s %s HTTP/1.0\r\n", command, url);
	bufp += strlen(bufp);
	if(user){
		/* Add basic authentication:
		 * Authorization: Basic [user:password,base64 encoded]
		 * or Proxy-Authorization: Basic [user:password,base64 encoded]
		 */
		sprintf(bufp, "%s: Basic %s\r\n",
			proxy? "Proxy-Authorization":"Authorization",
			encode(&buff[1000], user));
		bufp += strlen(bufp);
	}
	/* Posting a file? -- Put the length in the header */
	if(poster && (fppost= fopen(poster, "rb"))){
		/* Get ntot= length of file to post */
		fseek(fppost, 0, SEEK_END);
		ntot= ftell(fppost);
		fseek(fppost, 0, SEEK_SET);

		sprintf(bufp, "Content-Length: %d\r\n", ntot);
		bufp += strlen(bufp);
	}
	/* Send more header stuff */
	if(headextra && (fp= fopen(headextra, "rt"))){
		cnt= &buff[sizeof(buff)] - bufp;
		while(cnt>128 && fgets(bufp, cnt, fp)){
			int k= strlen(bufp);
			if(k>1){
				bufp += k+1;
				strcpy(bufp-2, "\r\n");
				cnt -= k;
			}
		}
		fclose(fp);
	}
	strcpy(bufp, "\r\n");
	bufp += 2;
	if(verbose)
		printf("...Sending:\n%s", buff);

	do_send(fclient, buff, bufp-buff);
	if(fppost){
		if(verbose)
			printf("...posting %d bytes\n", ntot);
		while(ntot>0){
			/* Read a buffer-full of the file to post */
			cnt= fread(buff, 1, ntot<sizeof(buff)? ntot:sizeof(buff), fppost);
			if(cnt<=0)
				break;
			ntot -= cnt;
			/* Send the buffer */
			do_send(fclient, buff, cnt);
		}
		fclose(fppost);
	}
	if(verbose)
		printf("...Receiving:\n");
	if((cnt = recv(fclient, buff, sizeof(buff), 0)) == SOCKET_ERROR)
		errormsg("Error reading data from http daemon");
	if(cnt == 0)
		errormsg("No data received from http daemon");
	do{
		int k;
		static int neol;

		/* Count successive ends-of-line to find end of header */
		for(k= 0; k<cnt; k++){
			char c= buff[k];
			if(c==0x0a)
				neol += 2;
			else if(c==0x0d)
				neol += 1;
			else
				neol= 0;
			putc(c, fpout);
			if((neol==4 || neol==6) && outname){
				/* Found end of header
				 * The header has been written to the console
				 * Now write the rest of the stuff to outname
				 */
				fflush(fpout);

				fpout= fopen(outname, "wb");
				if(fpout==0)
					fpout= stdout;
				outname= 0;
			}
		}
		fflush(fpout);
		cnt= recv(fclient, buff, sizeof(buff), 0);

		/* If the call blocked... */
		if(cnt==SOCKET_ERROR && WSAGetLastError()==WSAEWOULDBLOCK){
			fd_set *readfds;
			FD_ZERO(readfds);
			FD_SET(fclient, readfds);
			if(1==select(0, readfds, NULL, NULL, NULL))
				cnt= recv(fclient, buff, sizeof(buff), 0);
		}
	}while(cnt>0 || cnt==SOCKET_ERROR);
	fclose(fpout);
	errormsg(NULL);
}
