/* gs-domains.c
 *
 * part of the getstats program
 */


#include <stdio.h>
#include <string.h>
#include "gs-domains.h"

extern int
	uniquehostnum,
	largestdomnum,
	largestudomnum,
	printhtml,
	printbytes,
	toplines;
extern long
	largestbytenum;
extern char domain_report[];


char *doma[MAX_DOMAIN][2] = {
    {".ad",   "Andorra"},
    {".ae",   "United Arab Emirates"},
    {".af",   "Afghanistan"},
    {".ag",   "Antigua and Barbuda"},
    {".ai",   "Anguilla"},
    {".al",   "Albania"},
    {".am",   "Armenia"},
    {".an",   "Netherland Antilles"},
    {".ao",   "Angola"},
    {".aq",   "Antarctica"},
    {".ar",   "Argentina"},
    {".as",   "American Samoa"},
    {".at",   "Austria"},
    {".au",   "Australia"},
    {".aw",   "Aruba"},
    {".az",   "Azerbaidjan"},
    {".ba",   "Bosnia-Herzegovina"},
    {".bb",   "Barbados"},
    {".bd",   "Banglades"},
    {".be",   "Belgium"},
    {".bf",   "Burkina Faso"},
    {".bg",   "Bulgaria"},
    {".bh",   "Bahrain"},
    {".bi",   "Burundi"},
    {".bj",   "Benin"},
    {".bm",   "Bermuda"},
    {".bn",   "Brunei Darussalam"},
    {".bo",   "Bolivia"},
    {".br",   "Brazil"},
    {".bs",   "Bahamas"},
    {".bt",   "Buthan"},
    {".bv",   "Bouvet Island"},
    {".bw",   "Botswana"},
    {".by",   "Belarus"},
    {".bz",   "Belize"},
    {".ca",   "Canada"},
    {".cc",   "Cocos (Keeling) Islands"},
    {".cf",   "Central African Republic"},
    {".cg",   "Congo"},
    {".ch",   "Switzerland"},
    {".ci",   "Ivory Coast"},
    {".ck",   "Cook Islands"},
    {".cl",   "Chile"},
    {".cm",   "Cameroon"},
    {".cn",   "China"},
    {".co",   "Colombia"},
    {".cr",   "Costa Rica"},
    {".cs",   "Czechoslovakia"},
    {".cu",   "Cuba"},
    {".cv",   "Cape Verde"},
    {".cx",   "Christmas Island"},
    {".cy",   "Cyprus"},
    {".cz",   "Czech Republic"},
    {".de",   "Germany"},
    {".dj",   "Djibouti"},
    {".dk",   "Denmark"},
    {".dm",   "Dominica"},
    {".do",   "Dominican Republic"},
    {".dz",   "Algeria"},
    {".ec",   "Ecuador"},
    {".ee",   "Estonia"},
    {".eg",   "Egypt"},
    {".eh",   "Western Sahara"},
    {".es",   "Spain"},
    {".et",   "Ethiopia"},
    {".fi",   "Finland"},
    {".fj",   "Fiji"},
    {".fk",   "Falkland Islands (Malvinas)"},
    {".fm",   "Micronesia"},
    {".fo",   "Faroe Islands"},
    {".fr",   "France"},
    {".fx",   "France (European Territory)"},
    {".ga",   "Gabon"},
    {".gb",   "Great Britain (UK)"},
    {".gd",   "Grenada"},
    {".ge",   "Georgia"},
    {".gh",   "Ghana"},
    {".gi",   "Gibraltar"},
    {".gl",   "Greenland"},
    {".gp",   "Guadeloupe (French)"},
    {".gq",   "Equatorial Guinea"},
    {".gf",   "Guyana (French)"},
    {".gm",   "Gambia"},
    {".gn",   "Guinea"},
    {".gr",   "Greece"},
    {".gt",   "Guatemala"},
    {".gu",   "Guam (US)"},
    {".gw",   "Guinea Bissau"},
    {".gy",   "Guyana"},
    {".hk",   "Hong Kong"},
    {".hm",   "Heard and McDonald Islands"},
    {".hn",   "Honduras"},
    {".hr",   "Croatia"},
    {".ht",   "Haiti"},
    {".hu",   "Hungary"},
    {".id",   "Indonesia"},
    {".ie",   "Ireland"},
    {".il",   "Israel"},
    {".in",   "India"},
    {".io",   "British Indian Ocean Territory"},
    {".iq",   "Iraq"},
    {".ir",   "Iran"},
    {".is",   "Iceland"},
    {".it",   "Italy"},
    {".jm",   "Jamaica"},
    {".jo",   "Jordan"},
    {".jp",   "Japan"},
    {".ke",   "Kenya"},
    {".kg",   "Kirgistan"},
    {".kh",   "Cambodia"},
    {".ki",   "Kiribati"},
    {".km",   "Comoros"},
    {".kn",   "Saint Kitts Nevis Anguilla"},
    {".kp",   "North Korea"},
    {".kr",   "South Korea"},
    {".kw",   "Kuwait"},
    {".ky",   "Cayman Islands"},
    {".kz",   "Kazachstan"},
    {".la",   "Laos"},
    {".lb",   "Lebanon"},
    {".lc",   "Saint Lucia"},
    {".li",   "Liechtenstein"},
    {".lk",   "Sri Lanka"},
    {".lr",   "Liberia"},
    {".ls",   "Lesotho"},
    {".lt",   "Lithuania"},
    {".lu",   "Luxembourg"},
    {".lv",   "Latvia"},
    {".ly",   "Libya"},
    {".ma",   "Morocco"},
    {".mc",   "Monaco"},
    {".md",   "Moldavia"},
    {".mg",   "Madagascar"},
    {".mh",   "Marshall Islands"},
    {".ml",   "Mali"},
    {".mm",   "Myanmar"},
    {".mn",   "Mongolia"},
    {".mo",   "Macau"},
    {".mp",   "Northern Mariana Islands"},
    {".mq",   "Martinique (French)"},
    {".mr",   "Mauritania"},
    {".ms",   "Montserrat"},
    {".mt",   "Malta"},
    {".mu",   "Mauritius"},
    {".mv",   "Maldives"},
    {".mw",   "Malawi"},
    {".mx",   "Mexico"},
    {".my",   "Malaysia"},
    {".mz",   "Mozambique"},
    {".na",   "Namibia"},
    {".nc",   "New Caledonia (French)"},
    {".ne",   "Niger"},
    {".nf",   "Norfolk Island"},
    {".ng",   "Nigeria"},
    {".ni",   "Nicaragua"},
    {".nl",   "Netherlands"},
    {".no",   "Norway"},
    {".np",   "Nepal"},
    {".nr",   "Nauru"},
    {".nt",   "Neutral Zone"},
    {".nu",   "Niue"},
    {".nz",   "New Zealand"},
    {".om",   "Oman"},
    {".pa",   "Panama"},
    {".pe",   "Peru"},
    {".pf",   "Polynesia (French)"},
    {".pg",   "Papua New"},
    {".ph",   "Philippines"},
    {".pk",   "Pakistan"},
    {".pl",   "Poland"},
    {".pm",   "Saint Pierre and Miquelon"},
    {".pn",   "Pitcairn"},
    {".pt",   "Portugal"},
    {".pr",   "Puerto Rico (US)"},
    {".pw",   "Palau"},
    {".py",   "Paraguay"},
    {".qa",   "Qatar"},
    {".re",   "Reunion (French)"},
    {".ro",   "Romania"},
    {".ru",   "Russian Federation"},
    {".rw",   "Rwanda"},
    {".sa",   "Saudi Arabia"},
    {".sb",   "Solomon Islands"},
    {".sc",   "Seychelles"},
    {".sd",   "Sudan"},
    {".se",   "Sweden"},
    {".sg",   "Singapore"},
    {".sh",   "Saint Helena"},
    {".si",   "Slovenia"},
    {".sj",   "Svalbard and Jan Mayen Islands"},
    {".sk",   "Slovak Republic"},
    {".sl",   "Sierra Leone"},
    {".sm",   "San Marino"},
    {".sn",   "Senegal"},
    {".so",   "Somalia"},
    {".sr",   "Suriname"},
    {".st",   "Saint Tome and Principe"},
    {".su",   "Soviet Union"},
    {".sv",   "El Salvador"},
    {".sy",   "Syria"},
    {".sz",   "Swaziland"},
    {".tc",   "Turks and Caicos Islands"},
    {".td",   "Chad"},
    {".tf",   "French Southern Territory"},
    {".tg",   "Togo"},
    {".th",   "Thailand"},
    {".tj",   "Tadjikistan"},
    {".tk",   "Tokelau"},
    {".tm",   "Turkmenistan"},
    {".tn",   "Tunisia"},
    {".to",   "Tonga"},
    {".tp",   "East Timor"},
    {".tr",   "Turkey"},
    {".tt",   "Trinidad and Tobago"},
    {".tv",   "Tuvalu"},
    {".tw",   "Taiwan"},
    {".tz",   "Tanzania"},
    {".ua",   "Ukraine"},
    {".ug",   "Uganda"},
    {".uk",   "United Kingdom"},
    {".um",   "US Minor Outlying Islands"},
    {".us",   "United States"},
    {".uy",   "Uruguay"},
    {".uz",   "Uzbekistan"},
    {".va",   "Vatican City State"},
    {".vc",   "Saint Vincent and Grenadines"},
    {".ve",   "Venezuela"},
    {".vg",   "Virgin Islands (British)"},
    {".vi",   "Virgin Islands (US)"},
    {".vn",   "Vietnam"},
    {".vu",   "Vanuatu"},
    {".wf",   "Wallis and Futuna Islands"},
    {".ws",   "Samoa"},
    {".ye",   "Yemen"},
    {".yu",   "Yugoslavia"},
    {".za",   "South Africa"},
    {".zm",   "Zambia"},
    {".zr",   "Zaire"},
    {".zw",   "Zimbabwe"},
    {".arpa", "Old style Arpanet"},
    {".com",  "US Commercial"},
    {".edu",  "US Educational"},
    {".gov",  "US Government"},
    {".int",  "International"},
    {".mil",  "US Military"},
    {".nato", "Nato field"},
    {".net",  "Network"},
    {".org",  "Non-Profit"},
};



static int uniquedomnum = 0;
static struct domaintable *domtable[HASHSIZE];
static struct dnametable *dntable[HASHSIZE];


struct domainentry *adddomainentry(e, domain, date, filesize)
     struct domainentry *e;
     char *domain;
     long int date;
     long int filesize;
{
	if (e == NULL) {
		e = (struct domainentry *)
		emalloc(sizeof(struct domainentry));
		e->domain = (char *) strdup_g(domain);
		e->date = date;
		e->filesize = filesize;
		e->requestnum = 1;
		e->left = e->right = NULL;
	}
	else if (strcmp(domain, e->domain) > 0)
		e->left = adddomainentry(e->left, domain, date, filesize);
	else if (strcmp(domain, e->domain) < 0)
		e->right = adddomainentry(e->right, domain, date, filesize);
	else {
		e->date = date;
		e->filesize += filesize;
		e->requestnum += 1;
	}

	return e;
} /* adddomainentry */



struct domainentry *addsortdomainentry(e, domain, date, requestnum, filesize, unique)
     struct domainentry *e;
     char *domain;
     long int date;
     int requestnum;
     long int filesize;
     int unique;
{
	int isbigger;

	if (e == NULL) {
		e = (struct domainentry *)
		emalloc(sizeof(struct domainentry));
		e->domain = (char *) strdup_g(domain);
		e->date = date;
		e->filesize = filesize;
		e->requestnum = requestnum;
		e->unique = unique;
		e->left = e->right = NULL;
	}
	else {
		if (!strcmp(domain_report, "DOMAIN"))
			isbigger = (strcmp(domain, e->domain) > 0) ? 1 : 0;
		else if (!strcmp(domain_report, "DOMACCESS")) {
			isbigger = (requestnum < e->requestnum) ? 1 : 0;
			if (requestnum == e->requestnum)
				isbigger = (date < e->date) ? 1 : 0;
		}
		else if (!strcmp(domain_report, "DOMDATE")) {
			isbigger = (date < e->date) ? 1 : 0;
			if (date == e->date)
				isbigger = (requestnum < e->requestnum) ? 1 : 0;
		}
		else if (!strcmp(domain_report, "DOMBYTES")) {
			isbigger = (filesize < e->filesize) ? 1 : 0;
			if (filesize == e->filesize)
				isbigger = (strcmp(domain, e->domain) > 0) ?
				1 : 0;
		}
		else if (!strcmp(domain_report, "DOMUNIQ")) {
			isbigger = (unique < e->unique) ? 1 : 0;
			if (unique == e->unique)
				isbigger = (strcmp(domain, e->domain) > 0) ?
				1 : 0;
		}

		if (isbigger)
			e->left = addsortdomainentry(e->left, domain, date,
			requestnum, filesize, unique);
		else
			e->right = addsortdomainentry(e->right, domain,
			date, requestnum, filesize, unique);
	}

	return e;
} /* addsortdomainentry */



void sortdomains(e)
     struct domainentry *e;
{
	int i, u;

	if (e != NULL) {
		sortdomains(e->right);
		i = numstrchr(e->domain, '.');
		if (!i || i == 1)
			uniquedomnum++;
		if (e->filesize > largestbytenum)
			largestbytenum = e->filesize;
		if (e->requestnum > largestdomnum)
			largestdomnum = e->requestnum;
		u = hashuniqdn(e->domain);
		if (u > largestudomnum)
			largestudomnum = u;
		sorteddomainlist = (struct domainentry *)
		addsortdomainentry(sorteddomainlist, e->domain,
		e->date, e->requestnum, e->filesize, u);
		sortdomains(e->left);
	}
} /* sortdomains */



void printdomheader(report_type, levels, firstdate, lastdate)
     char *report_type;
     int levels;
     char *firstdate;
     char *lastdate;
{
	putchar('\n');
	if (printhtml) {
	  printf("<h2><a name=\"domain\">HTTP Server Domain Statistics</a>");
/*	  printf(" (See <a href=\"#general\">general</a> statistics)"); */
	  printf("</h2><i>\n");
	}
	else
		printf("HTTP Server Domain Statistics\n");
	printcovers(firstdate, lastdate);
	if (printhtml)
		printf("<br>");
	printf("%d level%s, sorted by ", levels, (levels == 1) ? "" : "s");
	if (!strcmp(report_type, "DOMAIN"))
		printf("domain name,");
	else if (!strcmp(report_type, "DOMACCESS"))
		printf("number of requests,");
	else if (!strcmp(report_type, "DOMUNIQ"))
		printf("number of unique domains,");
	else if (!strcmp(report_type, "DOMDATE"))
		printf("last access date,");
	else if (!strcmp(report_type, "DOMBYTES"))
		printf("byte traffic,");
	printf("%6d unique hosts, ", uniquehostnum);
	printf("%6d unique domains.", uniquedomnum);
	if (printhtml)
	      printf("</i><p>\n<%s>\n", REPORTTAG);
	else
	      putchar('\n');
	if (printbytes) {
		printf("# reqs : # uniq : Last Access (%s) : Bytes : ",
		DATEFORMAT);
		printf("Domain\n");
	}
	else
		printf("# reqs : # uniq : Last Access (%s) : Domain\n",
		DATEFORMAT);
	printbreak();
	putchar('\n');
} /* printdomheader */



void printdomains(e, domfieldlen, bytefieldlen, uniqfieldlen)
     struct domainentry *e;
     int domfieldlen;
     int bytefieldlen;
     int uniqfieldlen;
{
	int i, j;
	char date[LONGDATELEN];
	static int k;

	if (e != NULL) {
		printdomains(e->right, domfieldlen, bytefieldlen, uniqfieldlen);
		if (toplines && k++ >= toplines)
			return;
		i = domfieldlen - numlen(e->requestnum) - 1;
		while (i-- > 0)
			putchar(' ');
		printf("%d : ", e->requestnum);
		i = uniqfieldlen - numlen(e->unique) - 1;
		while (i-- > 0)
			putchar(' ');
		printf("%d : ", e->unique);

#ifdef SHOWSECONDS
		strcpy(date, getdatestr(e->date, 2));
		printf("%s : ", date);
#else
		strcpy(date, getdatestr(e->date, 3));
		printf("%s : ", date);
#endif
		if (printbytes) {
			i = bytefieldlen - numlen(e->filesize) - 1;
			if (i < 0)
				i = 1;
			while (i-- > 0)
				putchar(' ');
			printf("%d : ", e->filesize);
		}
		j = numstrchr(e->domain, '.') - 1;
		while (j-- > 0)
			putchar(' ');
		if (numstrchr(e->domain, '.') == 1 &&
		    hashlookupdom(e->domain) != NULL)
			printf("%s (%s)\n",
			       e->domain,
			       hashlookupdom(e->domain));
		else
			printf("%s\n", e->domain);
		fflush(stdout);
		printdomains(e->left, domfieldlen, bytefieldlen, uniqfieldlen);
	}
} /* printdomains */



void getdomain(address, date, level, filesize)
     char *address;
     long int date;
     int level;
     long int filesize;
{
	int i, maxlevel;
	char *s, *t, domain[DOMAINLEN], tempdomain[DOMAINLEN];

#ifndef SHOWIPDOMAINS
	if (isip(address)) {
		addhashdn(address);
		address = IPDOMSTR;
		adddomainentry(domainlist, address, date, filesize);
		return;
	}
#endif

	if (!strchr(address, '.')) {
		domainlist = (struct domainentry *)
		adddomainentry(domainlist, address, date, filesize);
		addhashdn(address);
		return;
	}

	domain[0] = '\0';
	maxlevel = numstrchr(address, '.') + 1;
	while (1) {
		t = (char *) strdup_g(address);
		if (!maxlevel)
			break;
		for (i = 0; i < maxlevel; i++)
			s = (char *) strtok((i) ? NULL : t, ".");
		if (s == NULL)
			break;
		sprintf(tempdomain, "%s.%s", domain, s);
		strcpy(domain, tempdomain);

		if (level-- > 0)
			domainlist = (struct domainentry *)
			adddomainentry(domainlist, domain, date, filesize);

		maxlevel--;
		free(t);
	}
	addhashdn(domain);
} /* getdomain */



void addhashdom(domain, description)
     char *domain;
     char *description;
{
	struct domaintable *dp;
	unsigned hashval;

	dp = (struct domaintable *) emalloc(sizeof(*dp));
	dp->domain = strdup_g(domain);
	hashval = hash(domain);
	dp->next = domtable[hashval];
	domtable[hashval] = dp;
	dp->description = strdup_g(description);
} /* addhashdomain */



char *hashlookupdom(domain)
     char *domain;
{
	struct domaintable *dp;

	for (dp = domtable[hash(domain)]; dp != NULL; dp = dp->next)
		if (strcmp(domain, dp->domain) == 0)
			return dp->description;
	return NULL;
} /* hashlookupdom */



void addhashdn(domain)
     char *domain;
{
	struct dnametable *dp;
	unsigned hashval;

	if (hashlookupdn(domain))
		return;

	dp = (struct dnametable *) emalloc(sizeof(*dp));
	dp->domain = strdup_g(domain);
	hashval = hash(domain);
	dp->next = dntable[hashval];
	dntable[hashval] = dp;
} /* addhashdn */



int hashlookupdn(domain)
     char *domain;
{
	struct dnametable *dp;

	for (dp = dntable[hash(domain)]; dp != NULL; dp = dp->next)
		if (strcmp(domain, dp->domain) == 0)
			return 1;
	return 0;
} /* hashlookupdn */



int hashuniqdn(domain)
     char *domain;
{
	int i, hits;
	struct dnametable *dp;

	for (i = hits = 0; i < HASHSIZE; i++)
		for (dp = dntable[i]; dp != NULL; dp = dp->next) {
#ifndef SHOWIPDOMAINS
			if (strcmp(domain, IPDOMSTR) == 0 &&
			isip(dp->domain)) {
				hits++;
				continue;
			}
#endif
			if ((char *) strstr(dp->domain, domain) == (char *)
			dp->domain)
				hits++;
		}
	return hits;
} /* hashuniqdn */
