/*
 *   Copyright (c) 2002 David Parsons. All rights reserved.
 *   
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *  3. All advertising materials mentioning features or use of this
 *     software must display the following acknowledgement:
 *     
 *   This product includes software developed by David Parsons
 *   (orc@pell.chi.il.us)
 *
 *  4. My name may not be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *     
 *  THIS SOFTWARE IS PROVIDED BY DAVID PARSONS ``AS IS'' AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 *  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 *  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID
 *  PARSONS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 *  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 *  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 *  THE POSSIBILITY OF SUCH DAMAGE.
 */

static const char rcsid[] = "$Id$";

/*
 * ethsetup() sets up ethernet on this machine (for network
 * installs)
 *
 * Two modes:  dhcp / manual
 *
 *     [ ] use dhcp
 *     [             ] ip        [              ] netmask
 *     [             ] gateway   [              ] dns
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dialog.h>
#include <ndialog.h>
#include "media.h"
#include "pathnames.h"

static int chk_dhcp(void*, void*);
static int chk_addr(void*, void*);
static int chk_ip(void*, void*);
static int finalcheck(void*, void*);

static void	*dhcp_ip,
		*addr_ip, 
		*netmask_ip,
		*gateway_ip,
		*dns_ip;

static char* dhcp_is_running = 0;

/* 
 * newNetMenu() generates the "Configure the network" menu
 */
void *
newNetMenu(void *m)
{
    void *tmp;
    struct ethmedia *eth = ETH(m);

    dhcp_ip = newCheck(0,0,0,"|Use dhcp to configure networking",&(eth->dhcp),chk_dhcp,0);
    addr_ip = newString(0,1,20,sizeof (eth->address),(eth->address),0,"Address   |",chk_addr,0);
    netmask_ip = newString(0,4,20,sizeof (eth->netmask), (eth->netmask),0,"Netmask   |",chk_ip,0);
    gateway_ip = newString(0,7,20,sizeof (eth->gateway), (eth->gateway),0,"Gateway   |",chk_ip,0);
    dns_ip = newString(0,10,20,sizeof (eth->dns), (eth->dns),0,"DNS server|",chk_ip,0);

    setUserData(dhcp_ip, m);
    chk_dhcp(dhcp_ip, 0);
    tmp = ObjChain(dhcp_ip,addr_ip);
    tmp = ObjChain(tmp,netmask_ip);
    tmp = ObjChain(tmp,gateway_ip);
    tmp = ObjChain(tmp,dns_ip);
    tmp = ObjChain(tmp,
		newOKButton(1, "OK", finalcheck, 0));
    tmp = ObjChain(tmp,
		newCancelButton(2, "CANCEL", 0, 0));
    return tmp;
} /* newAddMenu */


#define deleteNetMenu(x)	deleteObjChain(x)


static int
chk_dhcp(void* obj, void* win)
{
    char *flag = (char*)objDataPtr(obj);

    if (*flag) {
	setReadonly(addr_ip);
	setReadonly(netmask_ip);
	setReadonly(gateway_ip);
	setReadonly(dns_ip);
    }
    else {
	setWritable(addr_ip);
	setWritable(netmask_ip);
	setWritable(gateway_ip);
	setWritable(dns_ip);
    }
    return 1;
}


static char *
cidr2netmask(unsigned int width)
{
    static char bfr[80];
    int done = 0;
    int octets = width/8;
    int frac = width % 8;
    int sum, cur;

    bfr[0] = 0;
    while (octets > 0) {
	strcat(bfr, "255.");
	octets--;
	done++;
    }
    for (sum = 0, cur = 128; frac > 0; --frac) {
	sum += cur;
	cur /= 2;
    }
    sprintf(bfr+strlen(bfr), "%d", sum);

    while (done < 3) {
	strcat(bfr, ".0");
	done++;
    }
    return bfr;
}


static int
chk_addr(void* obj, void* win)
{
    unsigned int x1, x2, x3, x4, width;
    int ac;
    char *data = (char*)objDataPtr(obj);

    ac = sscanf(data, "%d.%d.%d.%d/%d", &x1, &x2, &x3, &x4, &width);

    if (ac == 5 || ac == 4) {
	if (x1 > 255 || x2 > 255 || x3 > 255 || x4 > 255)
	    goto bleah;
	if (ac == 5) {
	    if (width >= 32) {
		Error("Meaningless cidr width");
		return 0;
	    }
	    strcpy(objDataPtr(netmask_ip), cidr2netmask(width));
	    setReadonly(netmask_ip);
	}
	else
	    setWritable(netmask_ip);
	return 1;
    }
	
bleah:
    Error("Badly formatted IP address");
    return 0;
}


static int
chk_ip(void* obj, void* win)
{
    unsigned int x1, x2, x3, x4, ct;

    ct = sscanf((char*)objDataPtr(obj), "%d.%d.%d.%d", &x1, &x2, &x3, &x4);

    if (ct != 4 || x1 > 255 || x2 > 255 || x3 > 255 || x4 > 255) {
	Error(obj == netmask_ip ? "Badly formed netmask"
				: "Badly formatted IP address");
	return 1;
    }
    return 1;
}


static int
finalcheck(void* obj, void* win)
{
    void *m = getUserData(dhcp_ip);
    struct ethmedia *eth = ETH(m);
    int rc;
    char addr[20];
    char *p;

    if (eth->dhcp) {
	if (getpid() == 1) {
	    if (dhcp_is_running && strcmp(dhcp_is_running, mDev(m)) != 0)
		run(0, PATH_DHCLIENT, "dhcp", "-k", 0);

	    dialog_msgbox(INSTALLER, "Getting network settings\n"
				     "from DHCP server.", -1, -1, 0);
	    if (run(0, PATH_DHCLIENT, "dhcp", "-D", mDev(m), 0) != 0) {
		Error("Can't contact a dhcp server");
		return 0;
	    }
	    dhcp_is_running = mDev(m);
	}
	else
	    fprintf(stderr, "ethsetup: use DHCP on %s\n", mDev(m));
	return 1;
    }
    if (eth->address[0] == 0 || eth->netmask[0] == 0
			     || eth->gateway[0] == 0
			     || eth->dns[0] == 0) {
	Error("You need to specify all the network parameters if you're\n"
	      "Not using dhcp");
	return 1;

    }
    strcpy(addr, eth->address);
    if ((p = strchr(addr, '/')))
	*p = 0;

    setenv("ADDRESS", addr, 1);
    setenv("NETMASK", eth->netmask, 1);
    setenv("GATEWAY", eth->gateway, 1);
    setenv("DNS", eth->dns, 1);
    setenv("DEVICE", mDev(m), 1);
    if (getpid() == 1) {
	if (dhcp_is_running) {
	    run(0, PATH_DHCLIENT, "dhcp", "-k", 0);
	    dhcp_is_running = 0;
	}
	rc = run(0, "/sbin/network.sh", "network", "start", 0);
	if (rc != 0) {
	    Error("Cannot configure the network with this address");
	    return 1;
	}
    }
    else
	fprintf(stderr, "ethsetup: use /sbin/network.sh on %s\n", mDev(m));
    return 1;
}

/*
 * ethconfigure() brings up networking
 */
int
ethconfigure(void *m)
{
    void *chain;
    int rc;

    chain = newNetMenu(m);
    rc = MENU(chain, -1, -1, INSTALLER, 0, 0);
    deleteNetMenu(chain);
    return (rc == MENU_OK);
} /* ethconfigure */


/*
 * diy() choose a website to install mastodon from
 */
static int
diy(void *m)
{
    static char url[200];
    int rc;

    while (1) {
	rc = dialog_inputbox(INSTALLER, "Where is the Mastodon distribution?", -1, -1, url);

	if (rc == 0) {
	    ETH(m)->site = url;
	    ETH(m)->path = 0;
	    if (mOpen(m) == 0)
		mClose(m);
	    else {
		Error("Can't find a Mastodon distribution here");
		continue;
	    }
	}
	return (rc == 0);
    }
} /* diy */


static ListItem download[] = {
    { "ftp://dontuse.mastodon.pell.portland.or.us/pub/mastodon", "Development", 0, 0 },
    { "ftp://ftp.mastodon.pell.portland.or.us/pub/freeware/mastodon", "Pell (Oregon, USA)", 0, 0 },
    { 0, "Other", 0, 0 },
} ;
#define NR_DL (sizeof download/sizeof download[0])


#ifdef DEVELOPMENT
#   define Sites	download
#   define NrSites	NR_DL
#else
#   define Sites	(download+1)
#   define NrSites	(NR_DL-1)
#endif


/* 
 * ethsetup() lets you select the ftp/web site to download the distribution
 *            from.
 */
int
ethsetup(void *m)
{
    void *obj;
    int rc;
    int x;
    struct ethmedia *eth = ETH(m);

    obj = newRadioList(-1,0,-1,(NrSites>8)?8:NrSites, NrSites, Sites,
		    0/*no prompt*/,
		    "" /*no prefix, but we want to box the menu */,
		    CR_LIST|MENU_SELECTION|NO_HOTKEYS,
		    okaydone, 0);

    obj = ObjChain(0, obj);

    while (1) {
	rc = MENU(obj, -1, -1, INSTALLER, "Choose the web/ftp site to install\n"
					  "Mastodon " VERSION " from", 0);
	if (rc == MENU_OK) {
	    for (x=0; x < NrSites; x++)
		if (Sites[x].selected)
		    break;
	    if (x >= NrSites) {
		Error("Nothing selected?\n"
		      "This Shouldn't Happen!");
		continue;
	    }
	    if (Sites[x].id) {
		eth->site = Sites[x].id;
		eth->path = VERSION;
		break;
	    }
	    else if (diy(m))
		break;
	}
	else
	    break;
    }
    deleteObjChain(obj);
    return rc;
} /* ethsetup */
