#include <linux/if.h>
#include <linux/route.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>

#include <ctype.h>
#include <errno.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <resolv.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "bootpc.h"
#include "devices.h"
#include "install.h"
#include "log.h"
#include "newt.h"
#include "net.h"
#include "perror.h"
#include "windows.h"

struct intfconfig_s {
    newtComponent ipLabel, nmLabel, gwLabel, nsLabel;
    newtComponent ipEntry, nmEntry, gwEntry, nsEntry;
    char * ip, * nm, * gw, * ns;
    char * bootpState;
};

struct netconfig_s {
    newtComponent nsLabels[3], hnLabel, dnLabel;
    newtComponent nsEntrys[3], hnEntry, dnEntry;
    char * ns[3], * gw, * hn, * dn;
};

static void ipCallback(newtComponent co, void * data);
static void hnCallback(newtComponent co, void * data);

/* It's a bit gross, but setupNetworkInterface() will setup your general
   networking information if bootp is used */
static int setupNetworkInterface(struct netInterface * intf,
				 struct netConfig * netc,
			         struct driversLoaded ** dl, int justConfig);
static int doBringUpNetworking(struct netInterface * intf, 
			       struct netConfig * netc,
			       struct driversLoaded ** dl, int justConfig);
static int configureNetDevice(struct netInterface * intf);
/* intf in configureNetwork is optional */
static int configureNetwork(struct netConfig * netc, 
			    struct netInterface * intf);

static int doBootp(char * device, struct netInterface * intf,
		   struct netConfig * netc) {
    struct netInterface minInterface;
    int rc;

    if (testing) return 1;

    /* This is a bit of a hack, but it lets us use Jon Peatfield's bootp
        code w/o hacking at it */

    logMessage("using bootp for device %s", device);

    minInterface.ip = minInterface.netmask = minInterface.network = 0;
    minInterface.broadcast = 0xffffffff; 
    strcpy(minInterface.dev, device);

    if (configureNetDevice(&minInterface)) {
	logMessage("failed to setup minimal bootp interface %s", device);
	return 1;
    }

    winStatus(40, 3, "BOOTP", "Sending BOOTP request...");
    rc = performBootp(device, "255.255.255.255", "", 10, 0, NULL, 0, 1,
			BP_PUT_ENV);
    newtPopWindow();

    if (rc) {
	logMessage("performBootp() failed w/ return %d", rc);
	return 1;
    }

    if (!getenv("BOOTP_IPADDR")) {
	errorWindow("bootp query did not return an IP address");
	return 1;
    }
	
    /* These will be generated by bootpc.c even if they're not in the
       bootp reply */

    inet_aton(getenv("BOOTP_IPADDR"), (struct in_addr *) &intf->ip);
    inet_aton(getenv("BOOTP_NETMASK"), (struct in_addr *) &intf->netmask);
    inet_aton(getenv("BOOTP_BROADCAST"), (struct in_addr *) 
		    &intf->broadcast);
    inet_aton(getenv("BOOTP_NETWORK"), (struct in_addr *) 
		    &intf->network);

    intf->useBootp = 1;
    intf->isConfigured = 1;
    strcpy(intf->dev, device);

    if (getenv("BOOTP_GATEWAYS_1")) {
	strcpy(netc->defaultGateway, getenv("BOOTP_GATEWAYS_1"));
	netc->isConfigured = 1;
    }

    if (getenv("BOOTP_DNSSRVS_1")) {
	netc->nameserver[0] = strdup(getenv("BOOTP_DNSSRVS_1"));
	netc->isConfigured = 1;
	if (getenv("BOOTP_DNSSRVS_2"))  {
	    netc->nameserver[1] = strdup(getenv("BOOTP_DNSSRVS_2"));
	    if (getenv("BOOTP_DNSSRVS_3")) 
		netc->nameserver[2] = strdup(getenv("BOOTP_DNSSRVS_3"));
	}
    }

    /* 
	If bootp gave us a hostname and domain, use those. Otherwise,
	we'll try and query the nameserver a bit later on. If that fails
	as well, we'll try and use just the hostname BOOTP gave us.
    */
 
    if (getenv("BOOTP_HOSTNAME") && getenv("BOOTP_DOMAIN")) {
	logMessage("bootp returned hostname and domain name");
	strcpy(netc->hostname, getenv("BOOTP_HOSTNAME"));
	netc->isConfigured = 1;

	strcpy(netc->domainname, getenv("BOOTP_DOMAIN"));

	if (strcmp(netc->hostname, netc->domainname) &&
	    !strstr(netc->hostname, netc->domainname)) {
	    strcat(netc->hostname, ".");
	    strcat(netc->hostname, netc->domainname);
	}
    } 

    return 0;
}

/* The interface/gateway needs to be configured before this will work! */
static int guessHostname(struct netInterface * intf, struct netConfig * netc) {
    struct in_addr addr;
    char ips[50];

    if (intf->isUp && netc->nameserver[0]) {
	logMessage("using nameserver to find host and domain name");
	writeResolvConf("/etc", netc);
	memcpy(&addr, &intf->ip, sizeof(addr));
	strcpy(ips, inet_ntoa(addr));
	winStatus(40, 3, "Hostname", "Determing host name and domain...");
        if (in2host(ips, BP_PUT_ENV)) {
	    newtPopWindow();
	    logMessage("reverse name lookup failed");

	    if (getenv("BOOTP_HOSTNAME")) {
		strcpy(netc->hostname, getenv("BOOTP_HOSTNAME"));
	    } 
	} else {
	    newtPopWindow();
	    logMessage("reverse name lookup worked");
	    strcpy(netc->hostname, getenv("BOOTP_HOSTFULL"));
	    strcpy(netc->domainname, getenv("BOOTP_HOSTDOMAIN"));
	}
    }

    return 0;
}

int addDefaultRoute(struct netConfig netc) {
    int s;
    struct rtentry route;
    struct sockaddr_in addr;

    if (testing) return 0;

    if (!strlen(netc.defaultGateway)) return 0;

    s = socket(AF_INET, SOCK_DGRAM, 0);
    if (s < 0) {
	close(s);
	errorWindow("socket: %s");
	return 1;
    }

    memset(&route, 0, sizeof(route));

    addr.sin_family = AF_INET;
    addr.sin_port = 0;
    inet_aton(netc.defaultGateway, &addr.sin_addr);
    memcpy(&route.rt_gateway, &addr, sizeof(addr));

    addr.sin_addr.s_addr = INADDR_ANY;
    memcpy(&route.rt_dst, &addr, sizeof(addr));
    memcpy(&route.rt_genmask, &addr, sizeof(addr));

    route.rt_flags = RTF_UP | RTF_GATEWAY;
    route.rt_metric = 0;

    if (ioctl(s, SIOCADDRT, &route)) {
	close(s);
	errorWindow("SIOCADDRT: %s");
	return 1;
    }

    return 0;
}

static int configureNetDevice(struct netInterface * intf) {
    struct ifreq req;
    struct rtentry route;
    int s;
    struct sockaddr_in addr;
    struct in_addr ia;
    char ip[20], nm[20], nw[20], bc[20];

    addr.sin_family = AF_INET;
    addr.sin_port = 0;

    memcpy(&ia, &intf->ip, sizeof(intf->ip));
    strcpy(ip, inet_ntoa(ia));

    memcpy(&ia, &intf->netmask, sizeof(intf->netmask));
    strcpy(nm, inet_ntoa(ia));

    memcpy(&ia, &intf->broadcast, sizeof(intf->broadcast));
    strcpy(bc, inet_ntoa(ia));

    memcpy(&ia, &intf->network, sizeof(intf->network));
    strcpy(nw, inet_ntoa(ia));

    logMessage("configuring %s ip: %s nm: %s nw: %s bc: %s", intf->dev,
		ip, nm, nw, bc);

    if (testing) return 0;
    
    strcpy(req.ifr_name, intf->dev);
    addr.sin_family = AF_INET;
    addr.sin_port = 0;
    memcpy(&addr.sin_addr, &intf->ip, sizeof(intf->ip));

    s = socket(AF_INET, SOCK_DGRAM, 0);
    if (s < 0) {
	errorWindow("socket: %s");
	return 1;
    }

    memcpy(&req.ifr_addr, &addr, sizeof(addr));
    if (ioctl(s, SIOCSIFADDR, &req)) {
	close(s);
	errorWindow("SIOCSIFADDR: %s");
	return 1;
    }

    memcpy(&addr.sin_addr, &intf->broadcast, sizeof(intf->broadcast));
    memcpy(&req.ifr_broadaddr, &addr, sizeof(addr));
    if (ioctl(s, SIOCSIFBRDADDR, &req)) {
	close(s);
	errorWindow("SIOCSIFNETMASK: %s");
	return 1;
    }

    memcpy(&addr.sin_addr, &intf->netmask, sizeof(intf->netmask));
    memcpy(&req.ifr_netmask, &addr, sizeof(addr));
    if (ioctl(s, SIOCSIFNETMASK, &req)) {
	close(s);
	errorWindow("SIOCSIFNETMASK: %s");
	return 1;
    }

    if (intf->isPtp)
	req.ifr_flags = IFF_UP | IFF_RUNNING | IFF_POINTOPOINT | IFF_NOARP;
    else
	req.ifr_flags = IFF_UP | IFF_RUNNING | IFF_BROADCAST;

    if (ioctl(s, SIOCSIFFLAGS, &req)) {
	close(s);
	errorWindow("SIOCSIFFLAGS: %s");
	return 1;
    }

    memset(&route, 0, sizeof(route));
    route.rt_dev = intf->dev;
    route.rt_flags = RTF_UP;

    memcpy(&addr.sin_addr, &intf->network, sizeof(intf->netmask));
    memcpy(&route.rt_dst, &addr, sizeof(addr));

    memcpy(&addr.sin_addr, &intf->netmask, sizeof(intf->netmask));
    memcpy(&route.rt_genmask, &addr, sizeof(addr));

    if (ioctl(s, SIOCADDRT, &route)) {
	close(s);
	errorWindow("SIOCADDRT: %s");
	return 1;
    }

    intf->isUp = 1;

    return 0;
}

int netDeviceAvailable(char * device) {
    struct ifreq req;
    int s;
    
    s = socket(AF_INET, SOCK_DGRAM, 0);
    if (s < 0) {
	close(s);
	errorWindow("socket: %s");
	return 1;
    }

    strcpy(req.ifr_name, device);

    if (ioctl(s, SIOCGIFFLAGS, &req)) {
	/* if we can't get the flags, the networking device isn't available */
	close(s);
	return 0;
    }
  
    return 1;
}

static void interfaceConfigToggle(newtComponent co, void * arg) {
    struct intfconfig_s * info = arg;
    int sense = NEWT_FLAGS_SET;

    if (*info->bootpState == ' ') 
	sense = NEWT_FLAGS_RESET;

    newtEntrySetFlags(info->ipEntry, NEWT_ENTRY_DISABLED, sense);
    newtEntrySetFlags(info->nmEntry, NEWT_ENTRY_DISABLED, sense);
    newtEntrySetFlags(info->gwEntry, NEWT_ENTRY_DISABLED, sense);
    newtEntrySetFlags(info->nsEntry, NEWT_ENTRY_DISABLED, sense);

    newtRefresh();
}

static int setupNetworkInterface(struct netInterface * intf,
				 struct netConfig * netc,
			         struct driversLoaded ** dl, int justConfig) {
    newtComponent okay, cancel, bootp, f, answer;
    int top = 4;
    struct intfconfig_s c;
    struct in_addr addr;
    int32 ptpNetmask;
    int rc;
    int done = 0;
    char useBootp;
    char * device = NULL;
    char * devices[] = { "eth0", "tr0", "plip0", "plip1", "plip2", NULL };
    char ** deviceptr;
    struct driversLoaded * adriver;

    /* I should probably ask which device to use if multiple ones are
       available -- oh well :-( */
    for (deviceptr = devices; *deviceptr; deviceptr++) {
	if (netDeviceAvailable(*deviceptr)) {
	    device = *deviceptr;
	    logMessage("%s is available -- using it for networking", device);
	    break;
	}
    }

    if (!device) {
	if ((rc = loadDeviceDriver(DRIVER_NET, dl)))
	    return rc;

	for (adriver = *dl; adriver && adriver->type != DRIVER_NET; 
		adriver = adriver->next);
	if (!adriver) {
	    logMessage("ack! loadDriveDriver() worked but didn't load a "
			"DRIVE_NET!!");
	    return INST_ERROR;
	}

	switch (adriver->minor) {
	  case DRIVER_MINOR_ETHERNET:	device = "eth0"; break;
	  case DRIVER_MINOR_TR:		device = "tr0"; break;
	  case DRIVER_MINOR_PLIP:	device = getPlipDeviceName(); break;
	  default:			exit(1); /* ack! */
	}
    }

    newtOpenWindow(15, 4, 50, 15, "Configure TCP/IP");

    c.ipLabel = newtLabel(1, top + 2, "IP address:");
    c.nmLabel = newtLabel(1, top + 3, "Netmask:");
    c.gwLabel = newtLabel(1, top + 4, "Default gateway (IP):");
    c.nsLabel = newtLabel(1, top + 5, "Primary nameserver:");
   
    if (!intf->ip){
	c.ipEntry = newtEntry(25, top + 2, "", 16, &c.ip, 0);
    } else {
	memcpy(&addr, &intf->ip, sizeof(intf->ip));
	c.ipEntry = newtEntry(25, top + 2, inet_ntoa(addr), 16, &c.ip, 0);
    }

    if (!intf->netmask){
	c.nmEntry = newtEntry(25, top + 3, "", 16, &c.nm, 0);
    } else {
	memcpy(&addr, &intf->netmask, sizeof(intf->netmask));
	c.nmEntry = newtEntry(25, top + 3, inet_ntoa(addr), 16, &c.nm, 0);
    }

    if (!netc->defaultGateway) 
	c.gwEntry = newtEntry(25, top + 4, "", 16, &c.gw, 0);
    else
	c.gwEntry = newtEntry(25, top + 4, netc->defaultGateway, 16, &c.gw, 0);


    if (!netc->nameserver[0]) 
	c.nsEntry = newtEntry(25, top + 5, "", 16, &c.ns, 0);
    else
	c.nsEntry = newtEntry(25, top + 5, netc->nameserver[0], 16, &c.gw, 0);
    
    c.bootpState = &useBootp;
   
    f = newtForm(NULL, NULL, 0);

    bootp = newtCheckbox(1, top, "Configure device with bootp", 
			 (intf->isConfigured && intf->useBootp) ? '*' : ' ', 
			 NULL, &useBootp);

    newtComponentAddCallback(bootp, interfaceConfigToggle, &c);

    newtFormAddComponents(f, bootp, c.ipLabel, c.nmLabel, c.gwLabel, c.nsLabel, 
			     c.ipEntry, c.nmEntry, c.gwEntry, c.nsEntry, NULL);

    if (c.bootpState)
	newtFormSetCurrent(f, c.ipEntry);

    newtComponentAddCallback(c.ipEntry, ipCallback, &c);
    newtComponentAddCallback(c.nmEntry, ipCallback, &c);

    okay = newtButton(8, top + 7, "Ok");
    cancel = newtButton(28, top + 7, "Cancel");

    newtFormAddComponents(f, okay, cancel, NULL);

    do {
	answer = newtRunForm(f);

	if (answer == cancel) {
	    newtFormDestroy(f);
	    newtPopWindow();

	    return INST_CANCEL;
	}

	if (useBootp != ' ') {
	    logMessage("Configuring device %s via bootp", device);
	    if (justConfig) {
		done = 1;
	    } else if (!doBootp(device, intf, netc)) {
		done = 1;
	    }
	} else {
	    strcpy(intf->dev, device);
	    inet_aton(c.ip, (struct in_addr *) &intf->ip);
	    inet_aton(c.nm, (struct in_addr *) &intf->netmask);
	    intf->broadcast = (intf->ip & intf->netmask) | (~intf->netmask);

	    inet_aton("255.255.255.255", (struct in_addr *) &ptpNetmask);

	    if (ptpNetmask == intf->netmask) {
		logMessage("configuring point to point device");
		inet_aton(c.gw, (struct in_addr *) &intf->network);
		intf->isPtp = 1;
	    } else {
		intf->network = intf->ip & intf->netmask;
		intf->isPtp = 0;
	    }

	    intf->isConfigured = 1;

	    strcpy(netc->defaultGateway, c.gw);

	    if (netc->nameserver[0]) free(netc->nameserver[0]);

	    if (strlen(c.ns))
		netc->nameserver[0] = strdup(c.ns);
	    else
		netc->nameserver[0] = NULL;
	    
	    netc->isConfigured = 1;

	    done = 1;
	}
    } while (!done);

    newtFormDestroy(f);
    newtPopWindow();

    if (!justConfig) {
	if (configureNetDevice(intf)) return INST_ERROR;
	if (addDefaultRoute(*netc)) return INST_ERROR;
    }

    return 0;
} 

static void hnCallback(newtComponent co, void * dptr) {
    char * buf;
    struct netconfig_s * data = dptr;

    if (strlen(data->hn)) return;
    if (!strlen(data->dn)) return;

    buf = alloca(strlen(data->dn) + 5);
    strcpy(buf, ".");
    strcat(buf, data->dn);
    
    newtEntrySet(data->hnEntry, buf, 0);
}

static void ipCallback(newtComponent co, void * dptr) {
    struct intfconfig_s * data = dptr;
    struct in_addr ipaddr, nmaddr, addr;
    char * ascii;
    int32 broadcast, network;

    if (co == data->ipEntry) {
	if (strlen(data->ip) && !strlen(data->nm)) {
	    if (inet_aton(data->ip, &ipaddr)) {
		ipaddr.s_addr = ntohl(ipaddr.s_addr);
		if (((ipaddr.s_addr & 0xFF000000) >> 24) <= 127)
		    ascii = "255.0.0.0";
		else if (((ipaddr.s_addr & 0xFF000000) >> 24) <= 191)
		    ascii = "255.255.0.0";
		else 
		    ascii = "255.255.255.0";
		newtEntrySet(data->nmEntry, ascii, 1);
	    }
	}
    } else if (co == data->nmEntry) {
	if (!strlen(data->ip) || !strlen(data->nm)) return;
	if (!inet_aton(data->ip, &ipaddr)) return;
	if (!inet_aton(data->nm, &nmaddr)) return;

        network = ipaddr.s_addr & nmaddr.s_addr;
	broadcast = (ipaddr.s_addr & nmaddr.s_addr) | (~nmaddr.s_addr);

	addr.s_addr = htonl(ntohl(broadcast) - 1);
	newtEntrySet(data->gwEntry, inet_ntoa(addr), 1);

	/* I'm such a lazy bastard */
	if ((ntohl(network) & 0xFFFFFF00) == 0xc7b71800) {
	    newtEntrySet(data->nsEntry, "199.183.24.1", 1);
	} else {
	    addr.s_addr = htonl(ntohl(network) + 1);
	    newtEntrySet(data->nsEntry, inet_ntoa(addr), 1);
	}
    }
}

int writeNetConfig(char * prefix, struct netConfig * netc, 
		   struct netInterface * gwdev, int verbose) {
    char filename[100];
    FILE * f;

    if (testing) return 0;

    sprintf(filename, "%s/network", prefix);

    f = fopen(filename, "w");
    if (!f) {
	errorWindow("cannot create network config file: %s");
	return INST_ERROR;
    }

    fprintf(f, "NETWORKING=yes\n");

    logMessage("writing network information to %s", filename);

    if (netc->hostname && strlen(netc->hostname)) {
	logMessage("\tnetwork is configured, writing complete information");
	fprintf(f, "HOSTNAME=%s\n", netc->hostname);
	fprintf(f, "DOMAINNAME=%s\n", netc->domainname);

	if (gwdev && strlen(netc->defaultGateway)) {
	    fprintf(f, "GATEWAY=%s\n", netc->defaultGateway);
	    fprintf(f, "GATEWAYDEV=%s\n", gwdev->dev);
	}

	if (verbose) {
	    if (netc->nameserver[0])
		fprintf(f, "NS1=%s\n", netc->nameserver[0]);
	    
	    if (netc->nameserver[1])
		fprintf(f, "NS2=%s\n", netc->nameserver[1]);
	    
	    if (netc->nameserver[2])
		fprintf(f, "NS3=%s\n", netc->nameserver[2]);
	}
    } else {
	logMessage("\tnetwork is not configured");
	fprintf(f, "HOSTNAME=localhost.localdomain\n");
    }

    fclose(f);

    return 0;
}

int writeHosts(char * prefix, struct netConfig * netc, 
	       struct netInterface * intf) {
    char filename[100];
    FILE * f;
    struct in_addr * addrptr;
    char * nickname;
    int i;

    if (testing) return 1;

    sprintf(filename, "%s/hosts", prefix);

    /* if /etc/hosts already exists, don't bother creating one */
    if (!access(filename, R_OK)) {
	logMessage("%s already exists -- won't crunch it", filename);
	return 0;
    }
    logMessage("%s doesn not exist -- creating", filename);

    f = fopen(filename, "w");
    if (!f) {
	errorWindow("cannot create /etc/hosts: %s");
	return INST_ERROR;
    }

    logMessage("writing host information to %s", filename);

    fprintf(f, "127.0.0.1\t\tlocalhost localhost.localdomain\n");
    if (netc->hostname && strlen(netc->hostname)) {
	addrptr = (struct in_addr *) &intf->ip;
	i = strlen(netc->hostname) - strlen(netc->domainname);
	if (i > 1 && !strcmp(netc->hostname + i, netc->domainname)
	    && netc->hostname[i - 1] == '.') {
	    nickname = strdup(netc->hostname);
	    nickname[i - 1] = '\0';
	    fprintf(f, "%s\t\t%s %s\n", inet_ntoa(*addrptr),
			netc->hostname, nickname);
	    free(nickname);
	} else {
	    fprintf(f, "%s\t\t%s\n", inet_ntoa(*addrptr),
			netc->hostname);
	}

	sethostname(netc->hostname, strlen(netc->hostname));
    }

    fclose(f);

    res_init();		/* reinit the resolver so DNS changes take affect */

    return 0;
}

int writeResolvConf(char * prefix, struct netConfig * netc) {
    char filename[100];
    FILE * f;

    if (testing) return 1;

    if (!netc->domainname && !netc->nameserver[0]) {
	logMessage("networking is not configured - not writing resolv.conf");
	return 0;
    }

    sprintf(filename, "%s/resolv.conf", prefix);

    f = fopen(filename, "w");
    if (!f) {
	errorWindow("cannot create resolv.conf: %s");
	return INST_ERROR;
    }

    logMessage("writing nameservices information to %s", filename);

    if (netc->domainname)
	fprintf(f, "search %s\n", netc->domainname);

    if (netc->nameserver[0])
	fprintf(f, "nameserver %s\n", netc->nameserver[0]);
    
    if (netc->nameserver[1])
	fprintf(f, "nameserver %s\n", netc->nameserver[1]);
    
    if (netc->nameserver[2])
	fprintf(f, "nameserver %s\n", netc->nameserver[2]);

    fclose(f);

    res_init();		/* reinit the resolver so DNS changes take affect */

    return 0;
}

int writeNetInterfaceConfig(char * prefix, struct netInterface * intf) {
    char filename[100];
    FILE * f;
    struct in_addr * addrptr;

    if (testing) return 0;

    if (!intf->isConfigured) {
	logMessage("network interface is not configured - not creating "
		   "ifcfg-*");
	return(0);
    }

    sprintf(filename, "%s/ifcfg-%s", prefix, intf->dev);

    f = fopen(filename, "w");
    if (!f) {
	errorWindow("cannot create network device config file: %s");
	return INST_ERROR;
    }

    logMessage("writing network information to %s", filename);
    
    fprintf(f, "DEVICE=%s\n", intf->dev);

    if (intf->useBootp) fprintf(f, "BOOTP=yes\n");

    addrptr = (struct in_addr *) &intf->ip;
    fprintf(f, "IPADDR=%s\n", inet_ntoa(*addrptr));

    addrptr = (struct in_addr *) &intf->netmask;
    fprintf(f, "NETMASK=%s\n", inet_ntoa(*addrptr));

    addrptr = (struct in_addr *) &intf->network;
    fprintf(f, "NETWORK=%s\n", inet_ntoa(*addrptr));

    addrptr = (struct in_addr *) &intf->broadcast;
    fprintf(f, "BROADCAST=%s\n", inet_ntoa(*addrptr));

    fprintf(f, "ONBOOT=yes\n");

    fclose(f);

    return 0;
}

int readNetInterfaceConfig(char * prefix, char * device, 
				struct netInterface * intf) {
    FILE * f;
    char * start, * end;
    char buf[250];
    int line = 0;

    intf->isConfigured = 0;

    if (testing) {
	return 0;
    }

    sprintf(buf, "%s/ifcfg-%s", prefix, device);

    f = fopen(buf, "r");
    if (!f) {
	if (errno != ENOENT) {
	    errorWindow("cannot open file: %s");
	    return INST_ERROR;
	} else {
	    intf->ip = 0;
	    return 0;
	}
    }

    while (fgets(buf, sizeof(buf) - 1, f)) {
	line++;

	start = buf;

	/* skipping leading spaces */
	while (*start && isspace(*start)) start++;
	if (!*start) continue;

	/* skip comments */
	if (*start == '#') continue;

	/* cut off trailing spaces and \n */
	end = start + strlen(start) - 2;
	while (isspace(*end)) end--;
	end++;
	*end = '\0';	
	end--;
	
	if (!strncmp("IPADDR=", start, 7)) {
	    start += 7;
	    inet_aton(start, (struct in_addr *) &intf->ip);
	} else if (!strncmp("DEVICE=", start, 7)) {
	    start += 7;
	    strcpy(intf->dev, start);
	} else if (!strncmp("BOOTP=", start, 6)) {
	    intf->useBootp = 1;
	} else if (!strncmp("NETMASK=", start, 8)) {
	    start += 8;
	    inet_aton(start, (struct in_addr *) &intf->netmask);
	} else if (!strncmp("NETWORK=", start, 8)) {
	    start += 8;
	    inet_aton(start, (struct in_addr *) &intf->network);
	    if (!strcmp(start, "255.255.255.255"))
		intf->isPtp = 1;
	} else if (!strncmp("BROADCAST=", start, 10)) {
	    start += 10;
	    inet_aton(start, (struct in_addr *) &intf->broadcast);
	}
    }

    fclose(f);

    intf->isConfigured = 1;

    return 0;
}

int configureNetwork(struct netConfig * netc, 
		     struct netInterface * intf) {
    struct netconfig_s n;
    int top = 3;
    newtComponent f, okay, cancel, answer;
    int i;

    newtOpenWindow(15, 4, 50, 15, "Configure Network");

    n.dnLabel = newtLabel(1, top    , "Domain name:");
    n.hnLabel = newtLabel(1, top + 1, "Host name:");
    n.nsLabels[1] = newtLabel(1, top + 2, "Secondary nameserver (IP):");
    n.nsLabels[2] = newtLabel(1, top + 3, "Tertiary nameserver (IP):");

    n.dnEntry = newtEntry(28, top    , "", 20, &n.dn, NEWT_ENTRY_SCROLL);
    n.hnEntry = newtEntry(28, top + 1, "", 20, &n.hn, NEWT_ENTRY_SCROLL);
    n.nsEntrys[1] = newtEntry(28, top + 2, "", 20, &n.ns[1], NEWT_ENTRY_SCROLL);
    n.nsEntrys[2] = newtEntry(28, top + 3, "", 20, &n.ns[2], NEWT_ENTRY_SCROLL);

    if (!strlen(netc->hostname) && !strlen(netc->domainname) && intf->isUp && 
	netc->nameserver[0]) {
	guessHostname(intf, netc);
    } 

    if (netc->hostname)
	newtEntrySet(n.hnEntry, netc->hostname, 0);

    if (netc->domainname)
	newtEntrySet(n.dnEntry, netc->domainname, 0);

    if (netc->nameserver[1])
	newtEntrySet(n.nsEntrys[1], netc->nameserver[1], 0);
    if (netc->nameserver[2])
	newtEntrySet(n.nsEntrys[2], netc->nameserver[2], 0);

    newtComponentAddCallback(n.dnEntry, hnCallback, &n);

    f = newtForm(NULL, NULL, 0);
    newtFormAddComponents(f, n.dnLabel, n.hnLabel, n.nsLabels[1], 
			  n.nsLabels[2], NULL);
    newtFormAddComponents(f, n.dnEntry, n.hnEntry, n.nsEntrys[1], 
			  n.nsEntrys[2], NULL);

    okay = newtButton(11, 11, "Ok");
    cancel = newtButton(30, 11, "Cancel");

    newtFormAddComponents(f, okay, cancel, NULL);

    answer = newtRunForm(f);

    strcpy(netc->hostname, n.hn);
    strcpy(netc->domainname, n.dn);

    for (i = 1; i < 3; i++) {
	if (netc->nameserver[i]) 
	    free(netc->nameserver[i]);
	netc->nameserver[i] = *n.ns[i] ? strdup(n.ns[i]) : NULL;
    }

    newtFormDestroy(f);
    newtPopWindow();

    if (answer == cancel) return INST_CANCEL;

    netc->isConfigured = 1;

    return 0;
}

int readNetConfig(char * prefix, struct netConfig * netc) {
    FILE * f;
    char * start, * end;
    char buf[250];
    int line = 0;

    if (testing) {
	return 0;
    }

    sprintf(buf, "%s/network", prefix);

    f = fopen(buf, "r");
    if (!f) {
	if (errno != ENOENT) {
	    errorWindow("cannot open file: %s");
	    return INST_ERROR;
	} else {
	    netc->hostname[0] = '\0';
	    return 0;
	}
    }

    memset(netc, 0, sizeof(*netc));

    while (fgets(buf, sizeof(buf) - 1, f)) {
	line++;

	start = buf;

	/* skipping leading spaces */
	while (*start && isspace(*start)) start++;
	if (!*start) continue;

	/* skip comments */
	if (*start == '#') continue;

	/* cut off trailing spaces and \n */
	end = start + strlen(start) - 2;
	while (isspace(*end)) end--;
	end++;
	*end = '\0';	
	end--;
	
	if (!strncmp("HOSTNAME=", start, 9)) {
	    start += 9;
	    strcpy(netc->hostname, start);
	} else if (!strncmp("DOMAINNAME=", start, 11)) {
	    start += 11;
	    strcpy(netc->domainname, start);
	} else if (!strncmp("GATEWAY=", start, 8)) {
	    start += 8;
	    strcpy(netc->defaultGateway, start);
	} else if (!strncmp("NS1=", start, 4)) {
	    start += 4;
	    netc->nameserver[0] = strdup(start);
	} else if (!strncmp("NS2=", start, 4)) {
	    start += 4;
	    netc->nameserver[1] = strdup(start);
	} else if (!strncmp("NS3=", start, 4)) {
	    start += 4;
	    netc->nameserver[2] = strdup(start);
	}
    }

    fclose(f);

    netc->isConfigured = 1;

    return 0;
}


int bringUpNetworking(struct netInterface * intf, struct netConfig * netc,
		      struct driversLoaded ** dl) {
    return doBringUpNetworking(intf, netc, dl, 0);
}

#define BRINGUP_NET 	1
#define BRINGUP_CONF	2
#define BRINGUP_ROUTE	3
#define BRINGUP_DONE	4

static int doBringUpNetworking(struct netInterface * intf, 
			       struct netConfig * netc,
			       struct driversLoaded ** dl, int justConfig) {
    int where = BRINGUP_NET;
    int rc;

    while (where != BRINGUP_DONE) {
	switch (where) {
	  case BRINGUP_NET:
	    rc = setupNetworkInterface(intf, netc, dl, justConfig);
	    if (rc) return rc;
	    where = BRINGUP_CONF;
	    break;

	  case BRINGUP_CONF:
	    rc = configureNetwork(netc, intf);
	    if (rc == INST_CANCEL)
		where = BRINGUP_NET;
	    else if (rc)
		return INST_ERROR;
	    else
		where = BRINGUP_ROUTE;
	    break;

	  case BRINGUP_ROUTE:
	    if (justConfig)
		rc = 0;
	    else
		rc = addDefaultRoute(*netc);

	    if (rc) return rc;
	    where = BRINGUP_DONE;
	    break;
	}
    }

    /* write out the /etc/resolv.conf, as we'll need that to use name services
       properly */
    writeResolvConf("/etc", netc);

    return 0;
}

#define CHECKNET_CONFIG		1
#define CHECKNET_KEEP		2
#define CHECKNET_NONE		3
#define CHECKNET_NOBOOTP	4

static int checkNetConfigPanel(int isConfigured, int useBootp, int * choice) {
    newtComponent text, okay, cancel;
    newtComponent f, answer, yes, no, listbox;
    int which;

    if (isConfigured) {
	newtOpenWindow(18, 5, 42, 13, "Network Configuration");
	text = newtTextbox(1, 1, 40, 2, NEWT_TEXTBOX_WRAP);
	newtTextboxSetText(text,
		"LAN networking has already been configured. Do you "
		"want to:");

	listbox = newtListbox(8, 4, 0, NEWT_LISTBOX_RETURNEXIT);

	if (useBootp) {
	    newtListboxAddEntry(listbox, "Use bootp at startup", (void *) 1);
	    newtListboxAddEntry(listbox, "Use the current IP information", 
					  (void *) 4);
	    newtListboxAddEntry(listbox, "Reconfigure network now",
					  (void *) 2);
	    newtListboxAddEntry(listbox, "Don't setup networking", (void *) 3);
	} else {
	    newtListboxAddEntry(listbox, "Keep this setup", (void *) 1);
	    newtListboxAddEntry(listbox, "Reconfigure network now",
					  (void *) 2);
	    newtListboxAddEntry(listbox, "Don't setup networking", (void *) 3);
	}

	okay = newtButton(6, 9, "Ok");
	cancel = newtButton(23, 9, "Cancel");

	f = newtForm(NULL, NULL, 0);

	newtFormAddComponents(f, text, listbox, okay, cancel, NULL);

	answer = newtRunForm(f);
	if (answer == cancel) {
	    newtPopWindow();
	    newtFormDestroy(f);

	    return INST_CANCEL;
	}

	which = (int) newtListboxGetCurrent(listbox);

	newtPopWindow();
	newtFormDestroy(f);

	if (which == 1)
	    *choice = CHECKNET_KEEP;
	else if (which == 3)
	    *choice = CHECKNET_NONE;
	else if (which == 4)
	    *choice = CHECKNET_NOBOOTP;
	else
	    *choice = CHECKNET_CONFIG;
    } else {
	newtOpenWindow(18, 6, 42, 10, "Network Configuration");

	text = newtTextbox(1, 1, 40, 3, NEWT_TEXTBOX_WRAP);
	newtTextboxSetText(text,
		"Do you want to configure LAN (not dialup) networking "
		"for your installed system?");

	yes = newtButton(3, 6, " Yes ");
	no = newtButton(16, 6, "  No  ");
	cancel = newtButton(29, 6, "Cancel");

	f = newtForm(NULL, NULL, 0);
	newtFormAddComponents(f, text, yes, no, cancel, NULL);

	answer = newtRunForm(f);
	if (answer == f) 
	    answer = newtFormGetCurrent(f);

	newtPopWindow();
	newtFormDestroy(f);

	if (answer == yes) {
	    *choice = CHECKNET_CONFIG;
	} else if (answer == cancel) {
	    return INST_CANCEL;
	} else {
	    *choice = CHECKNET_NONE;
	}
    }

    return 0;
}

int checkNetConfig(struct netInterface * intf, struct netConfig * netc,
		   struct driversLoaded ** dl) {
    int choice, rc;

    /* this doesn't handle cancel very well as the dl makes it difficult :-( */

    do {
	rc = checkNetConfigPanel(netc->isConfigured && intf->isConfigured,
				 intf->useBootp, &choice);
	if (rc) return rc;

	switch (choice) {
	  case CHECKNET_NOBOOTP:
	    intf->useBootp = 0;
	    rc = 0;
	    break;

	  case CHECKNET_CONFIG:
	    rc = doBringUpNetworking(intf, netc, dl, 1);
	    if (rc == INST_ERROR) return rc;
	    break;

	  case CHECKNET_NONE:
	    intf->isConfigured = netc->isConfigured = 0;
	    rc = 0;
	    break;
	}
    } while (rc);

    return 0;
}
