/*
 * Copyright (C) 2001, 2002, and 2003  Roy Keene
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 *      email: libopennet@rkeene.org
 */

#include "compat.h"
#ifdef HAVE_SYS_UTSNAME_H
#include <sys/utsname.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#include <dirent.h>
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include "buffer.h"
#include "parse.h"
#include "crc.h"

static void strtolower(char *str) {
	uint32_t x=0;

	while (str[x]) { str[x]=tolower(str[x]); x++; }
}

static char *mimes64(unsigned char *str, int *size) {
	char *ret;
	char mimeabet[64]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	int i=0, x=0, m, bit_buf_sze;
	uint32_t bit_buf_sto;

	bit_buf_sze=bit_buffer_size(); /* Save the bit buffer, in case in use */
	bit_buf_sto=bit_buffer_read(bit_buf_sze);

	if ((ret=malloc((int) ((((float)(*size))*1.5)+7)))==NULL) return(NULL);

	while (i<(*size)) {
		while (bit_buffer_size()>=6) ret[x++]=mimeabet[bit_buffer_read(6)];
		if ((bit_buffer_size()+8)<=32) bit_buffer_write(str[i++],8);
	}
	while (bit_buffer_size()>=6) ret[x++]=mimeabet[bit_buffer_read(6)];
	if ((m=bit_buffer_size())) ret[x++]=mimeabet[bit_buffer_read(m)<<(6-m)];
	while (x&3) ret[x++]='=';

	bit_buffer_write(bit_buf_sto, bit_buf_sze); /* Restore it */

	*size=x;

	ret[x]='\0';

	return(ret);
}

char *mime64(unsigned char *str) {
	int x=strlen(str);
	return(mimes64(str,&x));
}

/* Parse those stupid netscape-style URLs, icky. 
 * And I know this REALLY crummy looking, but it works
 * (albeit, probably not perfectly) suffciently well.
 *  -- Roy Keene <rkeene@Rkeene.org>
 * `scheme' 	will never exceed 5 bytes
 * `username'	will never exceed 128 bytes
 * `password'	will never exceed 128 bytes
 * `host'		will never exceed 512 bytes
 * `file'		will never exceed 1024 bytes
 */
int parse_url(const char *url, char *scheme, char *username, char *password, char *host, int *port, unsigned char *file) {
	char *local_url=NULL, *local_url_s, *fbuf;
	int i;

	if (strstr(url,"://")==NULL) {	/* Make sure we have an URL */
		strncpy(file,url,1023);
		return(1);
	};

	local_url_s=local_url=strdup(url);

	*port=0;
	file[1]=0;

	strncpy(scheme,strsep(&local_url,":"),5);
	local_url+=2;
	strncpy(host,strsep(&local_url,"/"),512);
	if (local_url!=NULL) strncpy(file+1,local_url,1022);
	file[0]='/';

	fbuf=malloc(1024);
	file[0]='/';
	fbuf[0]='\0';
	for (i=0;i<strlen(file);i++) {
		if ((strlen(fbuf)+4)>=1023) break;
		if (file[i]<33 || file[i]>127) {
			if (file[i]==32) { strcat(fbuf, "+"); } else { sprintf(fbuf, "%s%%%02x", fbuf, file[i]); }
		} else {
			sprintf(fbuf, "%s%c", fbuf, file[i]);
		}
	}
	strncpy(file, fbuf, 1023);
	file[1023]=0;
	free(fbuf);


	password[0]=0;
	if (strchr(host,'@')!=NULL) {
		local_url=local_url_s; /* Doh!  This was missing. */
		strcpy(local_url,host);
		strncpy(username,strsep(&local_url,"@:"),128);
		if (strchr(local_url,'@')!=NULL)
			strncpy(password,strsep(&local_url,"@"),128);
		strcpy(host,local_url);
	} else {
		strcpy(username,"");
	}

	if (strchr(host,':')!=NULL) {
		local_url=local_url_s;
		strcpy(local_url,host);
		strcpy(host,strsep(&local_url,":"));
		*port=atoi(local_url);
	} else {
		if (!strcasecmp(scheme,"http")) *port=80;
		if (!strcasecmp(scheme,"ftp")) *port=21;
	}

	free(local_url_s);
	strtolower(scheme);
	return(0);
}


/*
	Replace 
		@@HOME@@	Home directory (getenv("HOME"))
		@@OSNM@@	OS Name (linux, freebsd, sunos, etc)
		@@OSVR@@	OS version (2.2.x, 4.2, 5.8, etc)
		@@OSVS@@	OS version (short) (2.2, 4.2, 5.8, etc)
		@@ARCH@@	Arch (i386, sparc64, sun4u, sun4m, etc)
		@@DIST@@	If OSNM=Linux, distribution of Linux.
		@@FILE@@	Name of compressed file.
		@@PASS@@	Prompt for Password
		@@USER@@	Prompt for Username
		@@ATSN@@	Put an `@'
*/
char *parse_url_subst(const char *src, const char *fname) {
	static struct utsname system_info;
	static int sysinfo_init=0;
	uint32_t cmd=0, x, strsz;
	const char *loc=src, *ploc=loc, *eloc;
	char *ret, *ret_s, found=0, *smbuf;

	if (!strstr((char *) src,"@@")) return(strdup(src));

	ret_s=ret=calloc(1024,1);
	eloc=src+strlen(src);

	if (!sysinfo_init) {
		uname(&system_info);
		strtolower(system_info.sysname);
		strtolower(system_info.machine);
		sysinfo_init=1;
	}


	ret[0]='\0';
	while (strstr(loc,"@@")) {
		cmd=elfcrc(0, loc=strstr(loc, "@@")+2, 4);
		loc+=6;
		if (loc>eloc) { loc=eloc-1; continue; }

		strsz=(loc-ploc-8);
		memcpy(ret, ploc, strsz);
		ret+=strsz;

		if ((ret-ret_s)>(1024-128)) break;

		switch (cmd) {
			case 288376: /* ARCH-done */
				x=strlen(system_info.machine);
				if (x>127) break;
				memcpy(ret,system_info.machine,x);
				ret+=x;
				break;
			case 346157: /* OSNM-done */
				x=strlen(system_info.sysname);
				if (x>127) break;
				memcpy(ret,system_info.sysname,x);
				ret+=x;
				break;
			case 346290: /* OSVR-done */
				if (strlen(system_info.release)>127) break;
				for (x=0;x<strlen(system_info.release);x++) {
					if (isdigit((int) system_info.release[x]) || system_info.release[x]=='.') {
						ret[0]=system_info.release[x];
						ret++;
					} else {
						break;
					}
				}
				break;
			case 346291: /* OSVS-done */
				if (strlen(system_info.release)>127) break;
				for (x=0;x<strlen(system_info.release);x++) {
					if (system_info.release[x]=='.' && found) break;
					if (system_info.release[x]=='.') found=1;
					if (isdigit((int) system_info.release[x]) || found) {
						ret[0]=system_info.release[x];
						ret++;
					}
				}
				break;
			case 306693: /* FILE-done */
				x=strlen(fname);
				if (x>127) break;
				memcpy(ret,fname,x);
				ret+=x;
				break;
			case 298628: /* DIST-done */
				if (!strcmp("linux",system_info.sysname)) {
					smbuf=parse_url_subst_dist();
					if (smbuf==NULL) break;
					x=strlen(smbuf);
					if (x>127) break;
					memcpy(ret, smbuf, x);
					ret+=x;
				}
				break;
			case 289150: /* ATSN-done */
				ret[0]='@';
				ret+=1;
				break;
			case 316437: /* HOME-done*/
				if (!(smbuf=getenv("HOME"))) break;
				x=strlen(smbuf);
				if (x>127) break;
				memcpy(ret, smbuf, x);
				ret+=x;
				break;
			default:
				break;
		}

		ploc=loc;
	}

	memcpy(ret, loc, strlen(loc));

	return(ret_s);
}

char *parse_url_subst_dist(void) {
	DIR *dirfd=NULL;
	struct dirent *info=NULL;
	static char retbuf[128]="unknown";
	char *buf;

/*
	Round 1: check for /etc/DISTRIBUTION-version or /etc/DISTRIBUTION-release
*/
	dirfd=opendir("/etc/.");
	while ((info=readdir(dirfd)) != NULL) {
		if ((buf=strstr(info->d_name,"-version"))!=NULL) {
			buf[0]=0;
			strncpy(retbuf, info->d_name, sizeof(retbuf));
			return(retbuf);
		}
		if ((buf=strstr(info->d_name,"_version"))!=NULL) {
			buf[0]=0;
			strncpy(retbuf, info->d_name, sizeof(retbuf));
			return(retbuf);
		}
		if ((buf=strstr(info->d_name,"-release"))!=NULL) {
			buf[0]=0;
			strncpy(retbuf, info->d_name, sizeof(retbuf));
			return(retbuf);
		}
	}

/*
	Round 2: ??? (distinguish between older versions of Slackware
		and Unknown distributions..)
*/

	return(retbuf);
}


ssize_t read_net(int fd, void *buf, size_t count) {
	int32_t i=0,x;

	while (i!=count) {
		x=read(fd,(void *) (((char *) buf)+i),count-i);
		if (x==0) break;
/* Damn typos, read_f was broken here because x<0 was typed as x>0 */
		if (x<0) return(x);
		i+=x;
	}

	return(i);
}

